| #include <functional> |
| #include <iostream> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include <cm/optional> |
| #include <cmext/string_view> |
| |
| #include <cm3p/json/value.h> |
| |
| #include "cmJSONHelpers.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; |
| }; |
| |
| enum class ErrorCode |
| { |
| Success, |
| InvalidInt, |
| InvalidBool, |
| InvalidString, |
| InvalidSubObject, |
| InvalidObject, |
| InvalidArray, |
| MissingRequired, |
| }; |
| |
| auto const IntHelper = |
| cmJSONIntHelper<ErrorCode>(ErrorCode::Success, ErrorCode::InvalidInt, 1); |
| auto const RequiredIntHelper = |
| cmJSONRequiredHelper<int, ErrorCode>(ErrorCode::MissingRequired, IntHelper); |
| auto const UIntHelper = |
| cmJSONUIntHelper<ErrorCode>(ErrorCode::Success, ErrorCode::InvalidInt, 1); |
| auto const BoolHelper = cmJSONBoolHelper<ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidBool, false); |
| auto const StringHelper = cmJSONStringHelper<ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidString, "default"); |
| auto const RequiredStringHelper = cmJSONRequiredHelper<std::string, ErrorCode>( |
| ErrorCode::MissingRequired, StringHelper); |
| auto const StringVectorHelper = cmJSONVectorHelper<std::string, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidArray, StringHelper); |
| auto const StringVectorFilterHelper = |
| cmJSONVectorFilterHelper<std::string, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidArray, StringHelper, |
| [](const std::string& value) { return value != "ignore"; }); |
| auto const StringMapHelper = cmJSONMapHelper<std::string, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidObject, StringHelper); |
| auto const StringMapFilterHelper = |
| cmJSONMapFilterHelper<std::string, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidObject, StringHelper, |
| [](const std::string& key) { return key != "ignore"; }); |
| auto const OptionalStringHelper = |
| cmJSONOptionalHelper<std::string>(ErrorCode::Success, StringHelper); |
| |
| bool testInt() |
| { |
| Json::Value v(2); |
| int i = 0; |
| ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::Success); |
| ASSERT_TRUE(i == 2); |
| |
| i = 0; |
| v = Json::nullValue; |
| ASSERT_TRUE(IntHelper(i, &v) == ErrorCode::InvalidInt); |
| |
| i = 0; |
| ASSERT_TRUE(IntHelper(i, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(i == 1); |
| |
| return true; |
| } |
| |
| bool testUInt() |
| { |
| Json::Value v(2); |
| unsigned int i = 0; |
| ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::Success); |
| ASSERT_TRUE(i == 2); |
| |
| i = 0; |
| v = Json::nullValue; |
| ASSERT_TRUE(UIntHelper(i, &v) == ErrorCode::InvalidInt); |
| |
| i = 0; |
| ASSERT_TRUE(UIntHelper(i, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(i == 1); |
| |
| return true; |
| } |
| |
| bool testBool() |
| { |
| Json::Value v(true); |
| bool b = false; |
| ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success); |
| ASSERT_TRUE(b); |
| |
| b = false; |
| v = false; |
| ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::Success); |
| ASSERT_TRUE(!b); |
| |
| b = false; |
| v = 4; |
| ASSERT_TRUE(BoolHelper(b, &v) == ErrorCode::InvalidBool); |
| |
| b = true; |
| ASSERT_TRUE(BoolHelper(b, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(!b); |
| |
| return true; |
| } |
| |
| bool testString() |
| { |
| Json::Value v("str"); |
| std::string str = ""; |
| ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::Success); |
| ASSERT_TRUE(str == "str"); |
| |
| str = ""; |
| v = Json::nullValue; |
| ASSERT_TRUE(StringHelper(str, &v) == ErrorCode::InvalidString); |
| |
| str = ""; |
| ASSERT_TRUE(StringHelper(str, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(str == "default"); |
| |
| return true; |
| } |
| |
| bool testObject() |
| { |
| auto const subhelper = |
| cmJSONObjectHelper<ObjectStruct, ErrorCode>(ErrorCode::Success, |
| ErrorCode::InvalidSubObject) |
| .Bind("subfield"_s, &ObjectStruct::Field2, IntHelper); |
| auto const helper = cmJSONObjectHelper<ObjectStruct, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidObject) |
| .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) |
| .Bind("field2"_s, subhelper) |
| .Bind<std::string>("field3"_s, nullptr, StringHelper); |
| |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = Json::objectValue; |
| v["field2"]["subfield"] = 2; |
| v["field3"] = "world!"; |
| v["extra"] = "extra"; |
| |
| ObjectStruct s1; |
| ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); |
| ASSERT_TRUE(s1.Field1 == "Hello"); |
| ASSERT_TRUE(s1.Field2 == 2); |
| |
| v["field2"]["subfield"] = "wrong"; |
| ObjectStruct s2; |
| ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt); |
| |
| v["field2"].removeMember("subfield"); |
| ObjectStruct s3; |
| ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidSubObject); |
| |
| v.removeMember("field2"); |
| ObjectStruct s4; |
| ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidObject); |
| |
| v["field2"] = Json::objectValue; |
| v["field2"]["subfield"] = 2; |
| v["field3"] = 3; |
| ObjectStruct s5; |
| ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidString); |
| |
| v.removeMember("field3"); |
| ObjectStruct s6; |
| ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject); |
| |
| v = "Hello"; |
| ObjectStruct s7; |
| ASSERT_TRUE(helper(s7, &v) == ErrorCode::InvalidObject); |
| |
| ObjectStruct s8; |
| ASSERT_TRUE(helper(s8, nullptr) == ErrorCode::InvalidObject); |
| |
| return true; |
| } |
| |
| bool testObjectInherited() |
| { |
| auto const helper = |
| cmJSONObjectHelper<InheritedStruct, ErrorCode>(ErrorCode::Success, |
| ErrorCode::InvalidObject) |
| .Bind("field1"_s, &InheritedStruct::Field1, StringHelper) |
| .Bind("field2"_s, &InheritedStruct::Field2, IntHelper) |
| .Bind("field3"_s, &InheritedStruct::Field3, StringHelper); |
| |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = 2; |
| v["field3"] = "world!"; |
| v["extra"] = "extra"; |
| |
| InheritedStruct s1; |
| ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); |
| 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) == ErrorCode::InvalidInt); |
| |
| v.removeMember("field2"); |
| InheritedStruct s3; |
| ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject); |
| |
| v["field2"] = 2; |
| v["field3"] = 3; |
| InheritedStruct s4; |
| ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString); |
| |
| v.removeMember("field3"); |
| InheritedStruct s5; |
| ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject); |
| |
| v = "Hello"; |
| InheritedStruct s6; |
| ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject); |
| |
| InheritedStruct s7; |
| ASSERT_TRUE(helper(s7, nullptr) == ErrorCode::InvalidObject); |
| |
| return true; |
| } |
| |
| bool testObjectNoExtra() |
| { |
| auto const helper = cmJSONObjectHelper<ObjectStruct, ErrorCode>( |
| ErrorCode::Success, ErrorCode::InvalidObject, false) |
| .Bind("field1"_s, &ObjectStruct::Field1, StringHelper) |
| .Bind("field2"_s, &ObjectStruct::Field2, IntHelper); |
| |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = 2; |
| |
| ObjectStruct s1; |
| ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); |
| ASSERT_TRUE(s1.Field1 == "Hello"); |
| ASSERT_TRUE(s1.Field2 == 2); |
| |
| v["extra"] = "world!"; |
| ObjectStruct s2; |
| ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidObject); |
| |
| return true; |
| } |
| |
| bool testObjectOptional() |
| { |
| auto const helper = |
| cmJSONObjectHelper<ObjectStruct, ErrorCode>(ErrorCode::Success, |
| ErrorCode::InvalidObject) |
| .Bind("field1"_s, &ObjectStruct::Field1, StringHelper, false) |
| .Bind("field2"_s, &ObjectStruct::Field2, IntHelper, false) |
| .Bind<std::string>("field3_s", nullptr, StringHelper, false); |
| |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = 2; |
| v["field3"] = "world!"; |
| v["extra"] = "extra"; |
| |
| ObjectStruct s1; |
| ASSERT_TRUE(helper(s1, &v) == ErrorCode::Success); |
| ASSERT_TRUE(s1.Field1 == "Hello"); |
| ASSERT_TRUE(s1.Field2 == 2); |
| |
| v = Json::objectValue; |
| ObjectStruct s2; |
| ASSERT_TRUE(helper(s2, &v) == ErrorCode::Success); |
| ASSERT_TRUE(s2.Field1 == "default"); |
| ASSERT_TRUE(s2.Field2 == 1); |
| |
| ObjectStruct s3; |
| ASSERT_TRUE(helper(s3, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(s3.Field1 == "default"); |
| ASSERT_TRUE(s3.Field2 == 1); |
| |
| return true; |
| } |
| |
| bool testVector() |
| { |
| Json::Value v(Json::arrayValue); |
| v.append("Hello"); |
| v.append("world!"); |
| v.append("ignore"); |
| |
| std::vector<std::string> l{ "default" }; |
| std::vector<std::string> expected{ "Hello", "world!", "ignore" }; |
| ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::Success); |
| ASSERT_TRUE(l == expected); |
| |
| v[1] = 2; |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidString); |
| |
| v = "Hello"; |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorHelper(l, &v) == ErrorCode::InvalidArray); |
| |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorHelper(l, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(l.empty()); |
| |
| return true; |
| } |
| |
| bool testVectorFilter() |
| { |
| Json::Value v(Json::arrayValue); |
| v.append("Hello"); |
| v.append("world!"); |
| v.append("ignore"); |
| |
| std::vector<std::string> l{ "default" }; |
| std::vector<std::string> expected{ |
| "Hello", |
| "world!", |
| }; |
| ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::Success); |
| ASSERT_TRUE(l == expected); |
| |
| v[1] = 2; |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidString); |
| |
| v = "Hello"; |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorFilterHelper(l, &v) == ErrorCode::InvalidArray); |
| |
| l = { "default" }; |
| ASSERT_TRUE(StringVectorFilterHelper(l, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(l.empty()); |
| |
| return true; |
| } |
| |
| bool testMap() |
| { |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = "world!"; |
| v["ignore"] = "ignore"; |
| |
| std::map<std::string, std::string> m{ { "key", "default" } }; |
| std::map<std::string, std::string> expected{ { "field1", "Hello" }, |
| { "field2", "world!" }, |
| { "ignore", "ignore" } }; |
| ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::Success); |
| ASSERT_TRUE(m == expected); |
| |
| v = Json::arrayValue; |
| m = { { "key", "default" } }; |
| ASSERT_TRUE(StringMapHelper(m, &v) == ErrorCode::InvalidObject); |
| |
| m = { { "key", "default" } }; |
| ASSERT_TRUE(StringMapHelper(m, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(m.empty()); |
| |
| return true; |
| } |
| |
| bool testMapFilter() |
| { |
| Json::Value v(Json::objectValue); |
| v["field1"] = "Hello"; |
| v["field2"] = "world!"; |
| v["ignore"] = "ignore"; |
| |
| std::map<std::string, std::string> m{ { "key", "default" } }; |
| std::map<std::string, std::string> expected{ { "field1", "Hello" }, |
| { "field2", "world!" } }; |
| ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::Success); |
| ASSERT_TRUE(m == expected); |
| |
| v = Json::arrayValue; |
| m = { { "key", "default" } }; |
| ASSERT_TRUE(StringMapFilterHelper(m, &v) == ErrorCode::InvalidObject); |
| |
| m = { { "key", "default" } }; |
| ASSERT_TRUE(StringMapFilterHelper(m, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(m.empty()); |
| |
| return true; |
| } |
| |
| bool testOptional() |
| { |
| Json::Value v = "Hello"; |
| |
| cm::optional<std::string> str{ "default" }; |
| ASSERT_TRUE(OptionalStringHelper(str, &v) == ErrorCode::Success); |
| ASSERT_TRUE(str == "Hello"); |
| |
| str.emplace("default"); |
| ASSERT_TRUE(OptionalStringHelper(str, nullptr) == ErrorCode::Success); |
| ASSERT_TRUE(str == cm::nullopt); |
| |
| return true; |
| } |
| |
| bool testRequired() |
| { |
| Json::Value v = "Hello"; |
| |
| std::string str = "default"; |
| int i = 1; |
| ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::Success); |
| ASSERT_TRUE(str == "Hello"); |
| ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::InvalidInt); |
| |
| v = 2; |
| str = "default"; |
| i = 1; |
| ASSERT_TRUE(RequiredStringHelper(str, &v) == ErrorCode::InvalidString); |
| ASSERT_TRUE(RequiredIntHelper(i, &v) == ErrorCode::Success); |
| ASSERT_TRUE(i == 2); |
| |
| str = "default"; |
| i = 1; |
| ASSERT_TRUE(RequiredStringHelper(str, nullptr) == |
| ErrorCode::MissingRequired); |
| ASSERT_TRUE(RequiredIntHelper(i, nullptr) == ErrorCode::MissingRequired); |
| |
| 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; |
| } |