diff options
-rw-r--r-- | src/core/common/Argument.cpp | 4 | ||||
-rw-r--r-- | src/core/common/Function.hpp | 23 | ||||
-rw-r--r-- | src/core/common/Variant.cpp | 6 | ||||
-rw-r--r-- | src/core/common/Variant.hpp | 15 | ||||
-rw-r--r-- | src/core/common/VariantConverter.cpp | 28 | ||||
-rw-r--r-- | src/core/model/Document.hpp | 2 | ||||
-rw-r--r-- | test/core/common/ArgumentTest.cpp | 441 |
7 files changed, 478 insertions, 41 deletions
diff --git a/src/core/common/Argument.cpp b/src/core/common/Argument.cpp index 78dd4b4..3461868 100644 --- a/src/core/common/Argument.cpp +++ b/src/core/common/Argument.cpp @@ -91,13 +91,13 @@ Argument Argument::String(std::string name, Argument Argument::Object(std::string name, const RttiType &type) { - return Argument(std::move(name), type, RttiTypes::None, nullptr, false); + return Argument(std::move(name), type, RttiTypes::None, Variant::fromObject(nullptr), false); } Argument Argument::Object(std::string name, const RttiType &type, std::nullptr_t) { - return Argument(std::move(name), type, RttiTypes::None, nullptr, true); + return Argument(std::move(name), type, RttiTypes::None, Variant::fromObject(nullptr), true); } Argument Argument::Function(std::string name) diff --git a/src/core/common/Function.hpp b/src/core/common/Function.hpp index 04030c8..79ee6b9 100644 --- a/src/core/common/Function.hpp +++ b/src/core/common/Function.hpp @@ -42,7 +42,7 @@ namespace ousia { */ class Function { protected: - Function() {}; + Function(){}; public: Function(const Function &) = delete; @@ -62,6 +62,23 @@ public: }; /** + * Function doing nothing. Instances of this class are used as default values + * for instances of the Function class. + */ +class FunctionStub : public Function { +public: + /** + * Constructor of the FunctionStub class. + */ + FunctionStub() {} + + Variant call(const Variant::arrayType &, void *) const override + { + return nullptr; + } +}; + +/** * The Method class refers to a method in the C++ code, belonging to an object * of a certain type T. * @@ -93,7 +110,7 @@ public: * * @param method is a pointer at the C++ function that should be called. */ - Method(Callback method) : method(method) {}; + Method(Callback method) : method(method){}; /** * Calls the underlying method. @@ -106,7 +123,7 @@ public: void *thisRef = nullptr) const override { // Call the method - return method(args, static_cast<T*>(thisRef)); + return method(args, static_cast<T *>(thisRef)); } }; } diff --git a/src/core/common/Variant.cpp b/src/core/common/Variant.cpp index e199bc7..81e6339 100644 --- a/src/core/common/Variant.cpp +++ b/src/core/common/Variant.cpp @@ -158,8 +158,10 @@ const RttiType& Variant::getRttiType() const return RttiTypes::Map; case VariantType::FUNCTION: return RttiTypes::Function; - case VariantType::OBJECT: - return asObject()->type(); + case VariantType::OBJECT: { + Variant::objectType o = asObject(); + return (o == nullptr) ? RttiTypes::Nullptr : o->type(); + } } return RttiTypes::None; } diff --git a/src/core/common/Variant.hpp b/src/core/common/Variant.hpp index 27dfda8..e5bca4d 100644 --- a/src/core/common/Variant.hpp +++ b/src/core/common/Variant.hpp @@ -342,6 +342,19 @@ public: Variant(mapType m) : ptrVal(nullptr) { setMap(std::move(m)); } /** + * Named constructor for object values. + * + * @param o is an object that can be converted to a Rooted handle. + */ + template<class T> + static Variant fromObject(T o) + { + Variant res; + res.setObject(o); + return res; + } + + /** * Named constructor for function values. * * @param f is a shared pointer pointing at the Function instance. @@ -900,7 +913,7 @@ public: * to a Rooted handle. */ template <class T> - void setObject(Handle<T> o) + void setObject(T o) { destroy(); type = VariantType::OBJECT; diff --git a/src/core/common/VariantConverter.cpp b/src/core/common/VariantConverter.cpp index 1f5f514..1c23c43 100644 --- a/src/core/common/VariantConverter.cpp +++ b/src/core/common/VariantConverter.cpp @@ -243,8 +243,11 @@ bool VariantConverter::toString(Variant &var, Logger &logger, Mode mode) // Print object address and type Variant::objectType obj = var.asObject(); std::stringstream ss; - ss << "<object " << obj.get() << " (" << obj->type().name << ")" - << ">"; + ss << "<object " << obj.get(); + if (obj.get() != nullptr) { + ss << " (" << obj->type().name << ")"; + } + ss << ">"; var = ss.str().c_str(); return true; } @@ -290,11 +293,7 @@ bool VariantConverter::toArray(Variant &var, const RttiType &innerType, for (Variant &v : var.asArray()) { res = convert(v, innerType, RttiTypes::None, logger, mode) & res; } - - // Return on successful conversion, otherwise output the default value - if (res) { - return true; - } + return res; } // No conversion possible, assign the default value and log an error @@ -321,11 +320,7 @@ bool VariantConverter::toMap(Variant &var, const RttiType &innerType, res = convert(e.second, innerType, RttiTypes::None, logger, mode) & res; } - - // Return on successful conversion, otherwise output the default value - if (res) { - return true; - } + return res; } // No conversion possible, assign the default value and log an error @@ -341,9 +336,8 @@ bool VariantConverter::toFunction(Variant &var, Logger &logger) } // No conversion possible, assign the default value and log an error - logger.error(msgUnexpectedType(var, VariantType::MAP)); - var.setFunction(std::shared_ptr<Function>{new Method<void>([]( - const Variant::arrayType &args, void *thisRef) { return Variant{}; })}); + logger.error(msgUnexpectedType(var, VariantType::FUNCTION)); + var.setFunction(std::shared_ptr<Function>{new FunctionStub()}); return false; } @@ -383,7 +377,7 @@ bool VariantConverter::convert(Variant &var, const RttiType &type, // obviously asked for a managed object. if (!var.isObject()) { logger.error(msgUnexpectedType(var, VariantType::OBJECT)); - var.setNull(); + var.setObject(nullptr); return false; } @@ -391,7 +385,7 @@ bool VariantConverter::convert(Variant &var, const RttiType &type, if (!var.getRttiType().isa(type)) { logger.error(std::string("Expected object of type ") + type.name + " but got object of type " + var.getRttiType().name); - var.setNull(); + var.setObject(nullptr); return false; } return true; diff --git a/src/core/model/Document.hpp b/src/core/model/Document.hpp index ab714b6..d22196a 100644 --- a/src/core/model/Document.hpp +++ b/src/core/model/Document.hpp @@ -334,8 +334,6 @@ public: : Node(mgr, std::move(name), parent) { } - - virtual ~StructureNode(){}; }; /** diff --git a/test/core/common/ArgumentTest.cpp b/test/core/common/ArgumentTest.cpp index 8976fba..d555cd8 100644 --- a/test/core/common/ArgumentTest.cpp +++ b/test/core/common/ArgumentTest.cpp @@ -20,8 +20,10 @@ #include <gtest/gtest.h> -#include <core/common/Logger.hpp> #include <core/common/Argument.hpp> +#include <core/common/Function.hpp> +#include <core/common/Logger.hpp> +#include <core/common/Rtti.hpp> #include <core/managed/Managed.hpp> @@ -30,10 +32,32 @@ namespace ousia { //static Logger logger; static TerminalLogger logger(std::cerr, true); +namespace { + +class TestManaged1 : public Managed { +public: + using Managed::Managed; +}; + +class TestManaged2 : public TestManaged1 { +public: + using TestManaged1::TestManaged1; +}; +} + +namespace RttiTypes { +static const Rtti<ousia::TestManaged1> TestManaged1 = + RttiBuilder("TestManaged1"); +static const Rtti<ousia::TestManaged2> TestManaged2 = + RttiBuilder("TestManaged2").parent(&TestManaged1); +} + TEST(Argument, validateBool) { Argument a = Argument::Bool("a"); + ASSERT_FALSE(a.hasDefault); + { Variant v{true}; ASSERT_TRUE(a.validate(v, logger)); @@ -60,16 +84,37 @@ TEST(Argument, validateBoolDefault) { Argument a = Argument::Bool("a", true); - Variant v{1}; - ASSERT_FALSE(a.validate(v, logger)); - ASSERT_TRUE(v.isBool()); - ASSERT_TRUE(v.asBool()); + ASSERT_TRUE(a.hasDefault); + ASSERT_TRUE(a.defaultValue.asBool()); + + { + Variant v{true}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + } + + { + Variant v{false}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isBool()); + ASSERT_FALSE(v.asBool()); + } + + { + Variant v{1}; + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isBool()); + ASSERT_TRUE(v.asBool()); + } } TEST(Argument, validateInt) { Argument a = Argument::Int("a"); + ASSERT_FALSE(a.hasDefault); + { Variant v{123}; ASSERT_TRUE(a.validate(v, logger)); @@ -89,16 +134,30 @@ TEST(Argument, validateIntDefault) { Argument a = Argument::Int("a", 42); - Variant v{1.1}; - ASSERT_FALSE(a.validate(v, logger)); - ASSERT_TRUE(v.isInt()); - ASSERT_EQ(42, v.asInt()); + ASSERT_TRUE(a.hasDefault); + ASSERT_EQ(42, a.defaultValue.asInt()); + + { + Variant v{123}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(123, v.asInt()); + } + + { + Variant v{1.1}; + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isInt()); + ASSERT_EQ(42, v.asInt()); + } } TEST(Argument, validateDouble) { Argument a = Argument::Double("a"); + ASSERT_FALSE(a.hasDefault); + { Variant v{123}; ASSERT_TRUE(a.validate(v, logger)); @@ -112,7 +171,7 @@ TEST(Argument, validateDouble) ASSERT_TRUE(v.isDouble()); ASSERT_EQ(1.1, v.asDouble()); } - + { Variant v{"1.0"}; ASSERT_FALSE(a.validate(v, logger)); @@ -125,16 +184,37 @@ TEST(Argument, validateDoubleDefault) { Argument a = Argument::Double("a", 42.0); - Variant v{"1.0"}; - ASSERT_FALSE(a.validate(v, logger)); - ASSERT_TRUE(v.isDouble()); - ASSERT_EQ(42.0, v.asDouble()); + ASSERT_TRUE(a.hasDefault); + ASSERT_EQ(42.0, a.defaultValue.asDouble()); + + { + Variant v{123}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(123.0, v.asDouble()); + } + + { + Variant v{1.1}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(1.1, v.asDouble()); + } + + { + Variant v{"1.0"}; + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isDouble()); + ASSERT_EQ(42.0, v.asDouble()); + } } TEST(Argument, validateString) { Argument a = Argument::String("a"); + ASSERT_FALSE(a.hasDefault); + { Variant v{"test"}; ASSERT_TRUE(a.validate(v, logger)); @@ -182,6 +262,44 @@ TEST(Argument, validateStringDefault) { Argument a = Argument::String("a", "test2"); + ASSERT_TRUE(a.hasDefault); + ASSERT_EQ("test2", a.defaultValue.asString()); + + { + Variant v{"test"}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isString()); + ASSERT_EQ("test", v.asString()); + } + + { + Variant v{true}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isString()); + ASSERT_EQ("true", v.asString()); + } + + { + Variant v{nullptr}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isString()); + ASSERT_EQ("null", v.asString()); + } + + { + Variant v{42}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isString()); + ASSERT_EQ("42", v.asString()); + } + + { + Variant v{42.5}; + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isString()); + ASSERT_EQ("42.5", v.asString()); + } + { Variant v{{1, 2, 3}}; ASSERT_FALSE(a.validate(v, logger)); @@ -190,5 +308,300 @@ TEST(Argument, validateStringDefault) } } +TEST(Argument, validateObject) +{ + Manager mgr; + Argument a = Argument::Object("a", RttiTypes::TestManaged1); + + ASSERT_FALSE(a.hasDefault); + + { + Rooted<Managed> m{new Managed(mgr)}; + Variant v(m); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } + + { + Rooted<TestManaged1> m{new TestManaged1(mgr)}; + Variant v(m); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(m, v.asObject()); + } + + { + Rooted<TestManaged2> m{new TestManaged2(mgr)}; + Variant v(m); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(m, v.asObject()); + } + + { + Rooted<TestManaged1> m1{nullptr}; + Variant v(m1); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } + + { + Variant v("test"); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } +} + +TEST(Argument, validateObjectDefault) +{ + Manager mgr; + Argument a = Argument::Object("a", RttiTypes::TestManaged1, nullptr); + + ASSERT_TRUE(a.hasDefault); + ASSERT_TRUE(a.defaultValue.isObject()); + ASSERT_EQ(nullptr, a.defaultValue.asObject()); + + { + Rooted<Managed> m{new Managed(mgr)}; + Variant v(m); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } + + { + Rooted<TestManaged1> m{new TestManaged1(mgr)}; + Variant v(m); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(m, v.asObject()); + } + + { + Rooted<TestManaged2> m{new TestManaged2(mgr)}; + Variant v(m); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(m, v.asObject()); + } + + { + Rooted<TestManaged1> m1{nullptr}; + Variant v(m1); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } + + { + Variant v("test"); + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isObject()); + ASSERT_EQ(nullptr, v.asObject()); + } +} + +static std::shared_ptr<Function> helloWorldFun{new Method<void>{[]( + const Variant::arrayType &arr, void *) { return Variant{"Hello World"}; }}}; + +static std::shared_ptr<Function> goodbyeWorldFun{ + new Method<void>{[](const Variant::arrayType &arr, + void *) { return Variant{"Goodbye Cruel World"}; }}}; + +TEST(Argument, validateFunction) +{ + Argument a = Argument::Function("a"); + + ASSERT_FALSE(a.hasDefault); + + { + Variant v = Variant::fromFunction(helloWorldFun); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isFunction()); + ASSERT_EQ("Hello World", v.asFunction()->call().asString()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isFunction()); + ASSERT_TRUE(v.asFunction()->call().isNull()); + } +} + +TEST(Argument, validateFunctionDefault) +{ + Argument a = Argument::Function("a", goodbyeWorldFun); + + ASSERT_TRUE(a.hasDefault); + ASSERT_TRUE(a.defaultValue.isFunction()); + ASSERT_EQ(goodbyeWorldFun, a.defaultValue.asFunction()); + + { + Variant v = Variant::fromFunction(helloWorldFun); + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isFunction()); + ASSERT_EQ("Hello World", v.asFunction()->call().asString()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isFunction()); + ASSERT_EQ("Goodbye Cruel World", v.asFunction()->call().asString()); + } +} + +TEST(Argument, validateArray) +{ + Argument a = Argument::Array("a"); + + ASSERT_FALSE(a.hasDefault); + + { + Variant::arrayType arr{1, "a", nullptr}; + Variant v{arr}; + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(arr, v.asArray()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(Variant::arrayType{}, v.asArray()); + } +} + +TEST(Argument, validateArrayDefault) +{ + Variant::arrayType arrDefault{1, "a", nullptr}; + Argument a = Argument::Array("a", arrDefault); + + ASSERT_TRUE(a.hasDefault); + ASSERT_TRUE(a.defaultValue.isArray()); + ASSERT_EQ(arrDefault, a.defaultValue.asArray()); + + { + Variant::arrayType arr{"test1", 42.5}; + Variant v{arr}; + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(arr, v.asArray()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(arrDefault, v.asArray()); + } +} + +TEST(Argument, validateArrayInner) +{ + Argument a = Argument::Array("a", RttiTypes::String); + + ASSERT_FALSE(a.hasDefault); + + { + Variant::arrayType arr{1, "a", nullptr}; + Variant v{arr}; + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(Variant::arrayType({"1", "a", "null"}), v.asArray()); + } + + { + Variant::arrayType arr{1, "a", Variant::fromObject(nullptr)}; + Variant v{arr}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(Variant::arrayType({"1", "a", ""}), v.asArray()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(Variant::arrayType{}, v.asArray()); + } +} + +TEST(Argument, validateArrayInnerDefault) +{ + Variant::arrayType arrDefault{1, "a", nullptr}; + Argument a = Argument::Array("a", RttiTypes::String, arrDefault); + + ASSERT_TRUE(a.hasDefault); + ASSERT_TRUE(a.defaultValue.isArray()); + ASSERT_EQ(arrDefault, a.defaultValue.asArray()); + + { + Variant::arrayType arr{"test1", 42.5}; + Variant v{arr}; + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(Variant::arrayType({"test1", "42.5"}), v.asArray()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isArray()); + ASSERT_EQ(arrDefault, v.asArray()); + } +} + +TEST(Argument, validateMap) +{ + Argument a = Argument::Map("a"); + + ASSERT_FALSE(a.hasDefault); + + { + Variant::mapType map{{"key1", 1}, {"key2", "a"}, {"key3", nullptr}}; + Variant v{map}; + + ASSERT_TRUE(a.validate(v, logger)); + ASSERT_TRUE(v.isMap()); + ASSERT_EQ(map, v.asMap()); + } + + { + Variant v{"foo"}; + + ASSERT_FALSE(a.validate(v, logger)); + ASSERT_TRUE(v.isMap()); + ASSERT_EQ(Variant::mapType{}, v.asMap()); + } +} } |