|
|
@@ -0,0 +1,493 @@
|
|
|
+#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,
|
|
|
+ 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 helper = cmJSONObjectHelper<ObjectStruct, ErrorCode>(
|
|
|
+ ErrorCode::Success, ErrorCode::InvalidObject)
|
|
|
+ .Bind("field1"_s, &ObjectStruct::Field1, StringHelper)
|
|
|
+ .Bind("field2"_s, &ObjectStruct::Field2, IntHelper)
|
|
|
+ .Bind<std::string>("field3"_s, nullptr, StringHelper);
|
|
|
+
|
|
|
+ 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["field2"] = "wrong";
|
|
|
+ ObjectStruct s2;
|
|
|
+ ASSERT_TRUE(helper(s2, &v) == ErrorCode::InvalidInt);
|
|
|
+
|
|
|
+ v.removeMember("field2");
|
|
|
+ ObjectStruct s3;
|
|
|
+ ASSERT_TRUE(helper(s3, &v) == ErrorCode::InvalidObject);
|
|
|
+
|
|
|
+ v["field2"] = 2;
|
|
|
+ v["field3"] = 3;
|
|
|
+ ObjectStruct s4;
|
|
|
+ ASSERT_TRUE(helper(s4, &v) == ErrorCode::InvalidString);
|
|
|
+
|
|
|
+ v.removeMember("field3");
|
|
|
+ ObjectStruct s5;
|
|
|
+ ASSERT_TRUE(helper(s5, &v) == ErrorCode::InvalidObject);
|
|
|
+
|
|
|
+ v = "Hello";
|
|
|
+ ObjectStruct s6;
|
|
|
+ ASSERT_TRUE(helper(s6, &v) == ErrorCode::InvalidObject);
|
|
|
+
|
|
|
+ ObjectStruct s7;
|
|
|
+ ASSERT_TRUE(helper(s7, 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;
|
|
|
+}
|