#include #include #include #include #include #include #include #include #include "cmJSONHelpers.h" #include "cmJSONState.h" #define ASSERT_TRUE(x) \ do { \ if (!(x)) { \ std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \ return false; \ } \ } while (false) namespace { struct ObjectStruct { std::string Field1; int Field2; }; struct InheritedStruct : public ObjectStruct { std::string Field3; }; namespace ErrorMessages { using ErrorGenerator = std::function; ErrorGenerator ErrorGeneratorBuilder(std::string errorMessage) { return [errorMessage](const Json::Value* value, cmJSONState* state) -> void { state->AddErrorAtValue(errorMessage, value); }; }; ErrorGenerator InvalidArray = ErrorGeneratorBuilder("Invalid Array"); ErrorGenerator MissingRequired = ErrorGeneratorBuilder("Missing Required"); ErrorGenerator InvalidMap = ErrorGeneratorBuilder("Invalid Map"); ErrorGenerator InvalidObject(JsonErrors::ObjectError /*errorType*/, const Json::Value::Members& extraFields) { return [extraFields](const Json::Value* value, cmJSONState* state) -> void { state->AddErrorAtValue("Invalid Object", value); }; }; }; using JSONHelperBuilder = cmJSONHelperBuilder; auto const IntHelper = JSONHelperBuilder::Int(1); auto const RequiredIntHelper = JSONHelperBuilder::Required(ErrorMessages::MissingRequired, IntHelper); auto const UIntHelper = JSONHelperBuilder::UInt(1); auto const BoolHelper = JSONHelperBuilder::Bool(false); auto const StringHelper = JSONHelperBuilder::String("default"); auto const RequiredStringHelper = JSONHelperBuilder::Required( ErrorMessages::MissingRequired, StringHelper); auto const StringVectorHelper = JSONHelperBuilder::Vector( ErrorMessages::InvalidArray, StringHelper); auto const StringVectorFilterHelper = JSONHelperBuilder::VectorFilter( ErrorMessages::InvalidArray, StringHelper, [](const std::string& value) { return value != "ignore"; }); auto const StringMapHelper = JSONHelperBuilder::Map(ErrorMessages::InvalidMap, StringHelper); auto const StringMapFilterHelper = JSONHelperBuilder::MapFilter( ErrorMessages::InvalidMap, StringHelper, [](const std::string& key) { return key != "ignore"; }); auto const OptionalStringHelper = JSONHelperBuilder::Optional(StringHelper); bool testInt() { Json::Value v(2); cmJSONState state; int i = 0; ASSERT_TRUE(IntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); i = 0; v = Json::nullValue; ASSERT_TRUE(!IntHelper(i, &v, &state)); i = 0; ASSERT_TRUE(IntHelper(i, nullptr, &state)); ASSERT_TRUE(i == 1); return true; } bool testUInt() { Json::Value v(2); cmJSONState state; unsigned int i = 0; ASSERT_TRUE(UIntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); i = 0; v = Json::nullValue; ASSERT_TRUE(!UIntHelper(i, &v, &state)); i = 0; ASSERT_TRUE(UIntHelper(i, nullptr, &state)); ASSERT_TRUE(i == 1); return true; } bool testBool() { Json::Value v(true); cmJSONState state; bool b = false; ASSERT_TRUE(BoolHelper(b, &v, &state)); ASSERT_TRUE(b); b = false; v = false; ASSERT_TRUE(BoolHelper(b, &v, &state)); ASSERT_TRUE(!b); b = false; v = 4; ASSERT_TRUE(!BoolHelper(b, &v, &state)); b = true; ASSERT_TRUE(BoolHelper(b, nullptr, &state)); ASSERT_TRUE(!b); return true; } bool testString() { Json::Value v("str"); cmJSONState state; std::string str = ""; ASSERT_TRUE(StringHelper(str, &v, &state)); ASSERT_TRUE(str == "str"); str = ""; v = Json::nullValue; ASSERT_TRUE(!StringHelper(str, &v, &state)); str = ""; ASSERT_TRUE(StringHelper(str, nullptr, &state)); ASSERT_TRUE(str == "default"); return true; } bool testObject() { auto const subhelper = JSONHelperBuilder::Object().Bind( "subfield"_s, &ObjectStruct::Field2, IntHelper); auto const helper = JSONHelperBuilder::Object() .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) .Bind("field2"_s, subhelper) .Bind("field3"_s, nullptr, StringHelper); Json::Value v(Json::objectValue); cmJSONState state; v["field1"] = "Hello"; v["field2"] = Json::objectValue; v["field2"]["subfield"] = 2; v["field3"] = "world!"; v["extra"] = "extra"; ObjectStruct s1; ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v["field2"]["subfield"] = "wrong"; ObjectStruct s2; ASSERT_TRUE(!helper(s2, &v, &state)); v["field2"].removeMember("subfield"); ObjectStruct s3; ASSERT_TRUE(!helper(s3, &v, &state)); v.removeMember("field2"); ObjectStruct s4; ASSERT_TRUE(!helper(s4, &v, &state)); v["field2"] = Json::objectValue; v["field2"]["subfield"] = 2; v["field3"] = 3; ObjectStruct s5; ASSERT_TRUE(!helper(s5, &v, &state)); v.removeMember("field3"); ObjectStruct s6; ASSERT_TRUE(!helper(s6, &v, &state)); v = "Hello"; ObjectStruct s7; ASSERT_TRUE(!helper(s7, &v, &state)); ObjectStruct s8; ASSERT_TRUE(!helper(s8, nullptr, &state)); return true; } bool testObjectInherited() { auto const helper = JSONHelperBuilder::Object(ErrorMessages::InvalidObject, true) .Bind("field1"_s, &InheritedStruct::Field1, StringHelper) .Bind("field2"_s, &InheritedStruct::Field2, IntHelper) .Bind("field3"_s, &InheritedStruct::Field3, StringHelper); Json::Value v(Json::objectValue); cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; v["field3"] = "world!"; v["extra"] = "extra"; InheritedStruct s1; ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); ASSERT_TRUE(s1.Field3 == "world!"); v["field2"] = "wrong"; InheritedStruct s2; ASSERT_TRUE(!helper(s2, &v, &state)); v.removeMember("field2"); InheritedStruct s3; ASSERT_TRUE(!helper(s3, &v, &state)); v["field2"] = 2; v["field3"] = 3; InheritedStruct s4; ASSERT_TRUE(!helper(s4, &v, &state)); v.removeMember("field3"); InheritedStruct s5; ASSERT_TRUE(!helper(s5, &v, &state)); v = "Hello"; InheritedStruct s6; ASSERT_TRUE(!helper(s6, &v, &state)); InheritedStruct s7; ASSERT_TRUE(!helper(s7, nullptr, &state)); return true; } bool testObjectNoExtra() { auto const helper = JSONHelperBuilder::Object( ErrorMessages::InvalidObject, false) .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) .Bind("field2"_s, &ObjectStruct::Field2, IntHelper); Json::Value v(Json::objectValue); cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; ObjectStruct s1; ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v["extra"] = "world!"; ObjectStruct s2; ASSERT_TRUE(!helper(s2, &v, &state)); return true; } bool testObjectOptional() { auto const helper = JSONHelperBuilder::Object(ErrorMessages::InvalidObject, true) .Bind("field1"_s, &ObjectStruct::Field1, StringHelper, false) .Bind("field2"_s, &ObjectStruct::Field2, IntHelper, false) .Bind("field3_s", nullptr, StringHelper, false); Json::Value v(Json::objectValue); cmJSONState state; v["field1"] = "Hello"; v["field2"] = 2; v["field3"] = "world!"; v["extra"] = "extra"; ObjectStruct s1; ASSERT_TRUE(helper(s1, &v, &state)); ASSERT_TRUE(s1.Field1 == "Hello"); ASSERT_TRUE(s1.Field2 == 2); v = Json::objectValue; ObjectStruct s2; ASSERT_TRUE(helper(s2, &v, &state)); ASSERT_TRUE(s2.Field1 == "default"); ASSERT_TRUE(s2.Field2 == 1); ObjectStruct s3; ASSERT_TRUE(helper(s3, nullptr, &state)); ASSERT_TRUE(s3.Field1 == "default"); ASSERT_TRUE(s3.Field2 == 1); return true; } bool testVector() { Json::Value v(Json::arrayValue); cmJSONState state; v.append("Hello"); v.append("world!"); v.append("ignore"); std::vector l{ "default" }; std::vector expected{ "Hello", "world!", "ignore" }; ASSERT_TRUE(StringVectorHelper(l, &v, &state)); ASSERT_TRUE(l == expected); v[1] = 2; l = { "default" }; ASSERT_TRUE(!StringVectorHelper(l, &v, &state)); v = "Hello"; l = { "default" }; ASSERT_TRUE(!StringVectorHelper(l, &v, &state)); l = { "default" }; ASSERT_TRUE(StringVectorHelper(l, nullptr, &state)); ASSERT_TRUE(l.empty()); return true; } bool testVectorFilter() { Json::Value v(Json::arrayValue); cmJSONState state; v.append("Hello"); v.append("world!"); v.append("ignore"); std::vector l{ "default" }; std::vector expected{ "Hello", "world!", }; ASSERT_TRUE(StringVectorFilterHelper(l, &v, &state)); ASSERT_TRUE(l == expected); v[1] = 2; l = { "default" }; ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state)); v = "Hello"; l = { "default" }; ASSERT_TRUE(!StringVectorFilterHelper(l, &v, &state)); l = { "default" }; ASSERT_TRUE(StringVectorFilterHelper(l, nullptr, &state)); ASSERT_TRUE(l.empty()); return true; } bool testMap() { Json::Value v(Json::objectValue); v["field1"] = "Hello"; v["field2"] = "world!"; v["ignore"] = "ignore"; cmJSONState state; std::map m{ { "key", "default" } }; std::map expected{ { "field1", "Hello" }, { "field2", "world!" }, { "ignore", "ignore" } }; ASSERT_TRUE(StringMapHelper(m, &v, &state)); ASSERT_TRUE(m == expected); v = Json::arrayValue; m = { { "key", "default" } }; ASSERT_TRUE(!StringMapHelper(m, &v, &state)); m = { { "key", "default" } }; ASSERT_TRUE(StringMapHelper(m, nullptr, &state)); ASSERT_TRUE(m.empty()); return true; } bool testMapFilter() { Json::Value v(Json::objectValue); cmJSONState state; v["field1"] = "Hello"; v["field2"] = "world!"; v["ignore"] = "ignore"; std::map m{ { "key", "default" } }; std::map expected{ { "field1", "Hello" }, { "field2", "world!" } }; ASSERT_TRUE(StringMapFilterHelper(m, &v, &state)); ASSERT_TRUE(m == expected); v = Json::arrayValue; m = { { "key", "default" } }; ASSERT_TRUE(!StringMapFilterHelper(m, &v, &state)); m = { { "key", "default" } }; ASSERT_TRUE(StringMapFilterHelper(m, nullptr, &state)); ASSERT_TRUE(m.empty()); return true; } bool testOptional() { Json::Value v = "Hello"; cmJSONState state; cm::optional str{ "default" }; ASSERT_TRUE(OptionalStringHelper(str, &v, &state)); ASSERT_TRUE(str == "Hello"); str.emplace("default"); ASSERT_TRUE(OptionalStringHelper(str, nullptr, &state)); ASSERT_TRUE(str == cm::nullopt); return true; } bool testRequired() { Json::Value v = "Hello"; std::string str = "default"; int i = 1; cmJSONState state; ASSERT_TRUE(RequiredStringHelper(str, &v, &state)); ASSERT_TRUE(str == "Hello"); ASSERT_TRUE(!RequiredIntHelper(i, &v, &state)); v = 2; str = "default"; i = 1; ASSERT_TRUE(!RequiredStringHelper(str, &v, &state)); ASSERT_TRUE(RequiredIntHelper(i, &v, &state)); ASSERT_TRUE(i == 2); str = "default"; i = 1; ASSERT_TRUE(!RequiredStringHelper(str, nullptr, &state)); ASSERT_TRUE(!RequiredIntHelper(i, nullptr, &state)); return true; } } int testJSONHelpers(int /*unused*/, char* /*unused*/[]) { if (!testInt()) { return 1; } if (!testUInt()) { return 1; } if (!testBool()) { return 1; } if (!testString()) { return 1; } if (!testObject()) { return 1; } if (!testObjectInherited()) { return 1; } if (!testObjectNoExtra()) { return 1; } if (!testObjectOptional()) { return 1; } if (!testVector()) { return 1; } if (!testVectorFilter()) { return 1; } if (!testMap()) { return 1; } if (!testMapFilter()) { return 1; } if (!testOptional()) { return 1; } if (!testRequired()) { return 1; } return 0; }