diff options
Diffstat (limited to 'lib/LuaBridge/Tests/Source')
-rw-r--r-- | lib/LuaBridge/Tests/Source/ClassTests.cpp | 1665 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/IssueTests.cpp | 145 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/IteratorTests.cpp | 96 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/LegacyTests.cpp | 354 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/ListTests.cpp | 77 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/LuaRefTests.cpp | 304 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/MapTests.cpp | 136 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/NamespaceTests.cpp | 250 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/PerformanceTests.cpp | 165 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/RefCountedPtrTests.cpp | 110 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/TestBase.h | 98 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/TestTypes.h | 122 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/Tests.cpp | 262 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/Tests.lua | 107 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/TestsMain.cpp | 19 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/UnorderedMapTests.cpp | 162 | ||||
-rw-r--r-- | lib/LuaBridge/Tests/Source/VectorTests.cpp | 99 |
17 files changed, 4171 insertions, 0 deletions
diff --git a/lib/LuaBridge/Tests/Source/ClassTests.cpp b/lib/LuaBridge/Tests/Source/ClassTests.cpp new file mode 100644 index 0000000..ab789f4 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/ClassTests.cpp @@ -0,0 +1,1665 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include <exception> +#include <map> +#include <memory> + +struct ClassTests : TestBase +{ + template <class T> + T variable (const std::string& name) + { + runLua ("result = " + name); + return result <T> (); + } +}; + +namespace { + +struct EmptyBase +{ +}; + +template <class T, class Base> +struct Class : Base +{ + Class () + : data () + { + } + + Class (T data) + : data (data) + { + } + + static Class <T, Base> staticFunction (Class <T, Base> value) + { + return value; + } + + std::string toString () const + { + std::ostringstream stream; + stream << data; + return stream.str (); + } + + bool operator== (const Class <T, Base>& rhs) const + { + return data == rhs.data; + } + + bool operator< (const Class <T, Base>& rhs) const + { + return data < rhs.data; + } + + bool operator<= (const Class <T, Base>& rhs) const + { + return data <= rhs.data; + } + + Class <T, Base> operator+ (const Class <T, Base>& rhs) const + { + return Class <T, Base> (data + rhs.data); + } + + Class <T, Base> operator- (const Class <T, Base>& rhs) const + { + return Class <T, Base> (data - rhs.data); + } + + Class <T, Base> operator* (const Class <T, Base>& rhs) const + { + return Class <T, Base> (data * rhs.data); + } + + Class <T, Base> operator/ (const Class <T, Base>& rhs) const + { + return Class <T, Base> (data / rhs.data); + } + + Class <T, Base> operator% (const Class <T, Base>& rhs) const + { + return Class <T, Base> (data % rhs.data); + } + + Class <T, Base> operator() (T param) + { + return Class <T, Base> (param); + } + + int len () const + { + return data; + } + + Class <T, Base> negate () const + { + return Class <T, Base> (-data); + } + + T method (T value) + { + return value; + } + + T methodState (T value, lua_State*) + { + return value; + } + + T constMethod (T value) const + { + return value; + } + + T getData () const + { + return data; + } + + void setData (T data) + { + this->data = data; + } + + T getDataState (lua_State*) const + { + return data; + } + + void setDataState (T data, lua_State*) + { + this->data = data; + } + + mutable T data; + static T staticData; +}; + +template <class T, class Base> +T Class <T, Base>::staticData = {}; + +} // namespace + +TEST_F (ClassTests, PassingUnregisteredClassToLuaThrows) +{ + using Unregistered = Class <int, EmptyBase>; + + runLua ("function process_fn (value) end"); + + auto process_fn = luabridge::getGlobal (L, "process_fn"); + ASSERT_TRUE (process_fn.isFunction ()); + + Unregistered value (1); + const Unregistered constValue (2); + ASSERT_THROW (process_fn (value), std::exception); + ASSERT_THROW (process_fn (constValue), std::exception); + ASSERT_THROW (process_fn (&value), std::exception); + ASSERT_THROW (process_fn (&constValue), std::exception); +} + +TEST_F (ClassTests, PassWrongClassFromLuaThrows) +{ + using Right = Class <int, EmptyBase>; + using WrongBase = Class <float, EmptyBase>; + using Wrong = Class <int, WrongBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Right> ("Right") + .endClass () + .beginClass <WrongBase> ("WrongBase") + .endClass () + .beginClass <Wrong> ("Wrong") + .addConstructor <void (*) (int)> () + .endClass () + .addFunction ("processRight", &Right::staticFunction); + + // bad argument #1 to 'processRight' (Right expected, got Wrong) + ASSERT_THROW (runLua ("result = processRight (Wrong (5))"), std::exception); + ASSERT_TRUE (result ().isNil ()); +} + +TEST_F (ClassTests, PassDerivedClassInsteadOfBase) +{ + using Base = Class <int, EmptyBase>; + using Derived = Class <float, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addConstructor <void (*) (float)> () + .endClass () + .addFunction ("processBase", &Base::staticFunction); + + runLua ("result = processBase (Derived (3.14))"); + ASSERT_EQ (0, result <Base> ().data); +} + +namespace { + +template <class T, class Base> +T processNonConst (Class <T, Base>* object) +{ + return object->data; +} + +} // namespace + +TEST_F (ClassTests, PassConstClassInsteadOfNonConstThrows) +{ + using Base = Class <int, EmptyBase>; + using Derived = Class <float, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass () + .addFunction ("processNonConst", &processNonConst <float, Base>); + + const Derived constObject (1.2f); + luabridge::setGlobal (L, &constObject, "constObject"); + + // bad argument #1 to 'processNonConst' (Derived expected, got const Derived) + ASSERT_THROW (runLua ("result = processNonConst (constObject)"), std::exception); + ASSERT_TRUE (result ().isNil ()); +} + +TEST_F (ClassTests, PassOtherTypeInsteadOfNonConstThrows) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () // Show that it does't matter + .endClass () + .addFunction ("processNonConst", &processNonConst <int, EmptyBase>); + + // bad argument #1 to 'processNonConst' (Int expected, got number) + ASSERT_THROW (runLua ("result = processNonConst (1)"), std::exception); + ASSERT_TRUE (result ().isNil ()); +} + +TEST_F (ClassTests, PassRegisteredClassInsteadOfUnregisteredThrows) +{ + using Int = Class <int, EmptyBase>; + using Float = Class <float, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Float> ("Float") + .addConstructor <void (*) (float)> () + .endClass () + .addFunction ("processUnregisteredInt", &Int::staticFunction); + + // bad argument #1 to 'processUnregisteredInt' (unregistered class expected, got Float) + ASSERT_THROW (runLua ("result = processUnregisteredInt (Float (1.2))"), std::exception); + ASSERT_TRUE (result ().isNil ()); +} + +namespace { + +Class <int, EmptyBase>& returnRef () +{ + static Class <int, EmptyBase> value (1); + return value; +} + +const Class <int, EmptyBase>& returnConstRef () +{ + return returnRef (); +} + +Class <int, EmptyBase>* returnPtr () +{ + return &returnRef (); +} + +const Class <int, EmptyBase>* returnConstPtr () +{ + return &returnConstRef (); +} + +Class <int, EmptyBase> returnValue () +{ + return Class <int, EmptyBase> (2); +} + +void addHelperFunctions (lua_State* L) +{ + luabridge::getGlobalNamespace (L) + .addFunction ("returnRef", &returnRef) + .addFunction ("returnConstRef", &returnConstRef) + .addFunction ("returnPtr", &returnPtr) + .addFunction ("returnConstPtr", &returnConstPtr) + .addFunction ("returnValue", &returnValue); +} + +} // namespace + +TEST_F (ClassTests, PassingUnregisteredClassFromLuaThrows) +{ + using Unregistered = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .addFunction ("returnRef", &returnRef) + .addFunction ("returnConstRef", &returnConstRef) + .addFunction ("returnPtr", &returnPtr) + .addFunction ("returnConstPtr", &returnConstPtr) + .addFunction ("returnValue", &returnValue); + + ASSERT_THROW (runLua ("result = returnRef ()"), std::exception); + ASSERT_THROW (runLua ("result = returnConstRef ()"), std::exception); + ASSERT_THROW (runLua ("result = returnPtr ()"), std::exception); + ASSERT_THROW (runLua ("result = returnConstPtr ()"), std::exception); + ASSERT_THROW (runLua ("result = returnValue ()"), std::exception); +} + +TEST_F (ClassTests, DeriveFromUnregisteredClassThrows) +{ + using Base = Class <int, EmptyBase>; + using Derived = Class <float, Base>; + + ASSERT_THROW ( + (luabridge::getGlobalNamespace (L).deriveClass <Derived, Base> ("Derived")), + std::exception); + + ASSERT_EQ (1, lua_gettop (L)); +} + +struct ClassFunctions : ClassTests +{ +}; + +TEST_F (ClassFunctions, MemberFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", &Int::method) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); +} + +TEST_F (ClassFunctions, MemberFunctions_PassState) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", &Int::methodState) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); +} + +TEST_F (ClassFunctions, ConstMemberFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("constMethod", &Int::constMethod) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():constMethod (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ():constMethod (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua ("result = returnPtr ():constMethod (3)"); + ASSERT_EQ (3, result <int> ()); + + runLua ("result = returnConstPtr ():constMethod (4)"); + ASSERT_EQ (4, result <int> ()); + + runLua ("result = returnValue ():constMethod (5)"); + ASSERT_EQ (5, result <int> ()); +} + +#ifdef LUABRIDGE_CXX11 + +namespace { + +template <class T, class Base> +T proxyFunction (Class <T, Base>* object, T value) +{ + object->data = value; + return value; +} + +template <class T, class Base> +T proxyFunctionState (Class <T, Base>* object, T value, lua_State*) +{ + object->data = value; + return value; +} + +template <class T, class Base> +T proxyConstFunction (const Class <T, Base>* object, T value) +{ + return value; +} + +} // namespace + +TEST_F (ClassFunctions, ProxyFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", &proxyFunction <int, EmptyBase>) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); +} + +TEST_F (ClassFunctions, ProxyFunctions_PassState) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", &proxyFunctionState <int, EmptyBase>) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); +} + +TEST_F (ClassFunctions, ConstProxyFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("constMethod", &proxyConstFunction <int, EmptyBase>) + .endClass (); + + addHelperFunctions (L); + + runLua ("result = returnRef ():constMethod (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ():constMethod (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua ("result = returnPtr ():constMethod (3)"); + ASSERT_EQ (3, result <int> ()); + + runLua ("result = returnConstPtr ():constMethod (4)"); + ASSERT_EQ (4, result <int> ()); + + runLua ("result = returnValue ():constMethod (5)"); + ASSERT_EQ (5, result <int> ()); +} + +TEST_F (ClassFunctions, StdFunctions) +{ + using Int = Class <int, EmptyBase>; + + auto sharedData = std::make_shared <int> (); + std::weak_ptr <int> data = sharedData; // Check __gc meta-method + + std::function <int (Int*, int)> function = [sharedData] (Int* object, int value) + { + object->data = value; + return value; + }; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", std::move(function)) + .endClass (); + + sharedData = nullptr; + ASSERT_FALSE (data.expired ()); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); + + runLua ("result = nil"); + lua_close (L); // Force garbage collection + L = nullptr; + + ASSERT_TRUE (data.expired()); +} + +TEST_F (ClassFunctions, StdFunctions_PassState) +{ + using Int = Class <int, EmptyBase>; + + auto sharedData = std::make_shared <int> (); + std::weak_ptr <int> data = sharedData; // Check __gc meta-method + + std::function <int (Int*, int, lua_State*)> function = [sharedData] (Int* object, int value, lua_State*) + { + object->data = value; + return value; + }; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("method", std::move(function)) + .endClass (); + + sharedData = nullptr; + ASSERT_FALSE (data.expired ()); + + addHelperFunctions (L); + + runLua ("result = returnRef ():method (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnPtr ():method (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua("result = returnConstPtr ().method"); // Don't call, just get + ASSERT_TRUE (result ().isNil ()); + + runLua ("result = returnValue ():method (3)"); + ASSERT_EQ (3, result <int> ()); + + runLua ("result = nil"); + lua_close (L); // Force garbage collection + L = nullptr; + + ASSERT_TRUE (data.expired()); +} + +TEST_F (ClassFunctions, ConstStdFunctions) +{ + using Int = Class <int, EmptyBase>; + + auto sharedData = std::make_shared <int> (); + std::weak_ptr <int> data = sharedData; // Check __gc meta-method + + std::function <int (const Int*, int)> function = [sharedData] (const Int* object, int value) + { + object->data = value; + return value; + }; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("constMethod", std::move(function)) + .endClass (); + + sharedData = nullptr; + ASSERT_FALSE (data.expired ()); + + addHelperFunctions (L); + + runLua ("result = returnRef ():constMethod (1)"); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = returnConstRef ():constMethod (2)"); + ASSERT_EQ (2, result <int> ()); + + runLua ("result = returnPtr ():constMethod (3)"); + ASSERT_EQ (3, result <int> ()); + + runLua ("result = returnConstPtr ():constMethod (4)"); + ASSERT_EQ (4, result <int> ()); + + runLua ("result = returnValue ():constMethod (5)"); + ASSERT_EQ (5, result <int> ()); + + runLua ("result = nil"); + lua_close (L); // Force garbage collection + L = nullptr; + + ASSERT_TRUE (data.expired()); +} + +#endif // LUABRIDGE_CXX11 + +struct ClassProperties : ClassTests +{ +}; + +TEST_F (ClassProperties, FieldPointers) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &Int::data, true) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = 2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (2, result () ["data"].cast <int> ()); + + runLua ("result = Int (42).data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (42, result <int> ()); +} + +TEST_F (ClassProperties, FieldPointers_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &Int::data, false) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + ASSERT_THROW (runLua ("result.data = 2"), std::exception); + + runLua ("result = Int (42).data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (42, result <int> ()); +} + +TEST_F (ClassProperties, MemberFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &Int::getData, &Int::setData) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = -2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (-2, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, MemberFunctions_PassState) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &Int::getDataState, &Int::setDataState) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = -2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (-2, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, MemberFunctions_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &Int::getData) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + ASSERT_THROW (runLua ("result.data = -2"), std::exception); + ASSERT_EQ (501, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, MemberFunctions_Derived) +{ + using Base = Class <std::string, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty ("data", &Base::getData, &Base::setData) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass (); + + Derived derived (12); + derived.Base::data = "abc"; + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isString ()); + ASSERT_EQ ("abc", result <std::string> ()); + + runLua ("derived.data = 5"); // Lua just casts integer to string + ASSERT_EQ ("5", derived.Base::data); + ASSERT_EQ (12, derived.data); + + runLua ("derived.data = '123'"); + ASSERT_EQ ("123", derived.Base::data); + ASSERT_EQ (12, derived.data); +} + +TEST_F (ClassProperties, MemberFunctions_Overridden) +{ + using Base = Class <float, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty ("data", &Base::getData, &Base::setData) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addProperty ("data", &Derived::getData, &Derived::setData) + .endClass (); + + Derived derived (50); + derived.Base::data = 1.23f; + luabridge::setGlobal (L, static_cast <Base*> (&derived), "base"); + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = base.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1.23f, result <float> ()); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (50, result <int> ()); + + runLua ("base.data = -3.14"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (50, derived.data); + + runLua ("derived.data = 7"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (7, derived.data); +} + +namespace { + +template <class T, class BaseClass> +T getData (const Class <T, BaseClass>* object) +{ + return object->data; +} + +template <class T, class BaseClass> +void setData (Class <T, BaseClass>* object, T data) +{ + object->data = data; +} + +} // namespace + +TEST_F (ClassProperties, ProxyFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &getData <int, EmptyBase>, &setData <int, EmptyBase>) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = -2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (-2, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, ProxyFunctions_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &getData <int, EmptyBase>) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + ASSERT_THROW (runLua ("result.data = -2"), std::exception); + ASSERT_EQ (501, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, ProxyFunctions_Derived) +{ + using Base = Class <std::string, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty ("data", &getData <std::string, EmptyBase>, &setData <std::string, EmptyBase>) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass (); + + Derived derived (12); + derived.Base::data = "abc"; + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isString ()); + ASSERT_EQ ("abc", result <std::string> ()); + + runLua ("derived.data = 5"); // Lua just casts integer to string + ASSERT_EQ ("5", derived.Base::data); + ASSERT_EQ (12, derived.data); + + runLua ("derived.data = '123'"); + ASSERT_EQ ("123", derived.Base::data); + ASSERT_EQ (12, derived.data); +} + +TEST_F (ClassProperties, ProxyFunctions_Overridden) +{ + using Base = Class <float, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty("data", &getData <float, EmptyBase>, &setData <float, EmptyBase>) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addProperty ("data", &getData <int, Base>, &setData <int, Base>) + .endClass (); + + Derived derived (50); + derived.Base::data = 1.23f; + luabridge::setGlobal (L, static_cast <Base*> (&derived), "base"); + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = base.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1.23f, result <float> ()); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (50, result <int> ()); + + runLua ("base.data = -3.14"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (50, derived.data); + + runLua ("derived.data = 7"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (7, derived.data); +} + +namespace { + +template <class T, class BaseClass> +int getDataC (lua_State* L) +{ + auto objectRef = luabridge::LuaRef::fromStack (L, 1); + auto* object = objectRef.cast <const Class <T, BaseClass>*> (); + luabridge::Stack <T>::push (L, object->data); + return 1; +} + +template <class T, class BaseClass> +int setDataC (lua_State* L) +{ + auto objectRef = luabridge::LuaRef::fromStack (L, 1); + auto* object = objectRef.cast <const Class <T, BaseClass>*>(); + auto valueRef = luabridge::LuaRef::fromStack (L, 2); + T value = valueRef.cast <T> (); + object->data = value; + return 0; +} + +} // namespace + +TEST_F (ClassProperties, ProxyCFunctions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &getDataC <int, EmptyBase>, &setDataC <int, EmptyBase>) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = -2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (-2, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, ProxyCFunctions_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", &getDataC <int, EmptyBase>) + .endClass (); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + ASSERT_THROW (runLua ("result.data = -2"), std::exception); + ASSERT_EQ (501, result () ["data"].cast <int> ()); +} + +TEST_F (ClassProperties, ProxyCFunctions_Derived) +{ + using Base = Class <std::string, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty ("data", &getDataC <std::string, EmptyBase>, &setDataC <std::string, EmptyBase>) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass (); + + Derived derived (12); + derived.Base::data = "abc"; + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isString ()); + ASSERT_EQ ("abc", result <std::string> ()); + + runLua ("derived.data = 5"); // Lua just casts integer to string + ASSERT_EQ ("5", derived.Base::data); + ASSERT_EQ (12, derived.data); + + runLua ("derived.data = '123'"); + ASSERT_EQ ("123", derived.Base::data); + ASSERT_EQ (12, derived.data); +} + +TEST_F (ClassProperties, ProxyCFunctions_Overridden) +{ + using Base = Class <float, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addProperty ("data", &getDataC <float, EmptyBase>, &setDataC <float, EmptyBase>) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addProperty ("data", &getData <int, Base>, &setData <int, Base>) + .endClass (); + + Derived derived (50); + derived.Base::data = 1.23f; + luabridge::setGlobal (L, static_cast <Base*> (&derived), "base"); + luabridge::setGlobal (L, &derived, "derived"); + + runLua ("result = base.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1.23f, result <float> ()); + + runLua ("result = derived.data"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (50, result <int> ()); + + runLua ("base.data = -3.14"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (50, derived.data); + + runLua ("derived.data = 7"); + ASSERT_EQ (-3.14f, derived.Base::data); + ASSERT_EQ (7, derived.data); +} + +#ifdef LUABRIDGE_CXX11 + +TEST_F (ClassProperties, StdFunctions) +{ + using Int = Class <int, EmptyBase>; + + auto sharedGetterData = std::make_shared <int> (); + std::weak_ptr <int> getterData = sharedGetterData; // Check __gc meta-method + + auto sharedSetterData = std::make_shared <int> (); + std::weak_ptr <int> setterData = sharedGetterData; // Check __gc meta-method + + std::function <int (const Int*)> getter = [sharedGetterData] (const Int* object) + { + return object->data; + }; + + std::function <void (Int*, int)> setter = [sharedSetterData] (Int* object, int value) + { + object->data = value; + }; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", std::move (getter), std::move (setter)) + .endClass (); + + sharedGetterData = nullptr; + ASSERT_FALSE (getterData.expired ()); + + sharedSetterData = nullptr; + ASSERT_FALSE (setterData.expired()); + + runLua ("result = Int (501)"); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result.data = -2"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (-2, result () ["data"].cast <int> ()); + + runLua ("result = nil"); + lua_close (L); // Force garbage collection + L = nullptr; + + ASSERT_TRUE (getterData.expired ()); + ASSERT_TRUE (setterData.expired ()); +} + +TEST_F (ClassProperties, StdFunctions_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + auto sharedGetterData = std::make_shared <int> (); + std::weak_ptr <int> getterData = sharedGetterData; // Check __gc meta-method + + std::function <int (const Int*)> getter = [sharedGetterData] (const Int* object) + { + return object->data; + }; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addProperty ("data", std::move (getter)) + .endClass (); + + sharedGetterData = nullptr; + ASSERT_FALSE (getterData.expired ()); + + runLua ("result = Int (501)"); + ASSERT_TRUE (result () ["data"].isNumber ()); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + ASSERT_THROW (runLua ("result.data = -2"), std::exception); + ASSERT_EQ (501, result () ["data"].cast <int> ()); + + runLua ("result = nil"); + lua_close (L); // Force garbage collection + L = nullptr; + + ASSERT_TRUE (getterData.expired ()); +} + +#endif // LUABRIDGE_CXX11 + +struct ClassStaticFunctions : ClassTests +{ +}; + +TEST_F (ClassStaticFunctions, Functions) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addStaticFunction ("static", &Int::staticFunction) + .endClass (); + + runLua ("result = Int.static (Int (35))"); + ASSERT_EQ (35, result <Int> ().data); +} + +TEST_F (ClassStaticFunctions, Functions_Derived) +{ + using Base = Class <std::string, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addConstructor <void (*) (std::string)> () + .addStaticFunction ("static", &Base::staticFunction) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass (); + + runLua ("result = Derived.static (Base ('abc'))"); + ASSERT_EQ ("abc", result <Base> ().data); +} + +TEST_F (ClassStaticFunctions, Functions_Overridden) +{ + using Base = Class <std::string, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addConstructor <void (*) (std::string)> () + .addStaticFunction ("staticFunction", &Base::staticFunction) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addConstructor <void (*) (int)> () + .addStaticFunction ("staticFunction", &Derived::staticFunction) + .endClass (); + + runLua ("result = Base.staticFunction (Base ('abc'))"); + ASSERT_EQ ("abc", result <Base> ().data); + + runLua ("result = Derived.staticFunction (Derived (123))"); + ASSERT_EQ (123, result <Derived> ().data); +} + +struct ClassStaticProperties : ClassTests +{ +}; + +TEST_F (ClassStaticProperties, FieldPointers) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addStaticProperty ("staticData", &Int::staticData, true) + .endClass (); + + Int::staticData = 10; + + runLua ("result = Int.staticData"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (10, result <int> ()); + + runLua ("Int.staticData = 20"); + ASSERT_EQ (20, Int::staticData); +} + +TEST_F (ClassStaticProperties, FieldPointers_ReadOnly) +{ + using Int = Class <int, EmptyBase>; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addStaticProperty ("staticData", &Int::staticData, false) + .endClass (); + + Int::staticData = 10; + + runLua ("result = Int.staticData"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (10, result <int> ()); + + ASSERT_THROW (runLua ("Int.staticData = 20"), std::exception); + ASSERT_EQ (10, Int::staticData); +} + +TEST_F (ClassStaticProperties, FieldPointers_Derived) +{ + using Base = Class <float, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addStaticProperty ("staticData", &Base::staticData, true) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .endClass (); + + Base::staticData = 1.23f; + Derived::staticData = 50; + + runLua ("result = Derived.staticData"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1.23f, result <float> ()); + + runLua ("Derived.staticData = -3.14"); + ASSERT_EQ (-3.14f, Base::staticData); + ASSERT_EQ (50, Derived::staticData); +} + +TEST_F (ClassStaticProperties, FieldPointers_Overridden) +{ + using Base = Class <float, EmptyBase>; + using Derived = Class <int, Base>; + + luabridge::getGlobalNamespace (L) + .beginClass <Base> ("Base") + .addStaticProperty ("staticData", &Base::staticData, true) + .endClass () + .deriveClass <Derived, Base> ("Derived") + .addStaticProperty ("staticData", &Derived::staticData, true) + .endClass (); + + Base::staticData = 1.23f; + Derived::staticData = 50; + + runLua ("result = Base.staticData"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1.23f, result <float> ()); + + runLua ("result = Derived.staticData"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (50, result <int> ()); + + runLua ("Base.staticData = -3.14"); + ASSERT_EQ (-3.14f, Base::staticData); + ASSERT_EQ (50, Derived::staticData); + + runLua ("Derived.staticData = 7"); + ASSERT_EQ (-3.14f, Base::staticData); + ASSERT_EQ (7, Derived::staticData); +} + +struct ClassMetaMethods : ClassTests +{ +}; + +TEST_F (ClassMetaMethods, __call) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__call", &Int::operator()) + .endClass (); + + runLua ("result = Int (1) (-1)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (-1, result <Int> ().data); + + runLua ("result = Int (2) (5)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (5, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __tostring) +{ + typedef Class <int, EmptyBase> Int; + typedef Class <std::string, EmptyBase> StringClass; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__tostring", &Int::toString) + .endClass () + .beginClass <StringClass> ("String") + .addConstructor <void (*) (std::string)> () + .addFunction ("__tostring", &StringClass::toString) + .endClass (); + + runLua ("result = tostring (Int (-123))"); + ASSERT_EQ ("-123", result <std::string> ()); + +#if LUA_VERSION_NUM >= 502 + // Lua 5.1 string.format doesn't use __tostring + runLua ("result = string.format ('%s%s', String ('abc'), Int (-123))"); + ASSERT_EQ ("abc-123", result <std::string> ()); +#endif +} + +TEST_F (ClassMetaMethods, __eq) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__eq", &Int::operator==) + .endClass (); + + runLua ("result = Int (1) == Int (1)"); + ASSERT_EQ (true, result <bool> ()); + + runLua ("result = Int (1) ~= Int (1)"); + ASSERT_EQ (false, result <bool> ()); + + runLua ("result = Int (1) == Int (2)"); + ASSERT_EQ (false, result <bool> ()); + + runLua ("result = Int (1) ~= Int (2)"); + ASSERT_EQ (true, result <bool> ()); +} + +TEST_F (ClassMetaMethods, __lt) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__lt", &Int::operator<) + .endClass (); + + runLua ("result = Int (1) < Int (1)"); + ASSERT_EQ (false, result <bool> ()); + + runLua ("result = Int (1) < Int (2)"); + ASSERT_EQ (true, result <bool> ()); + + runLua ("result = Int (2) < Int (1)"); + ASSERT_EQ (false, result <bool> ()); +} + +TEST_F (ClassMetaMethods, __le) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__le", &Int::operator<=) + .endClass (); + + runLua ("result = Int (1) <= Int (1)"); + ASSERT_EQ (true, result <bool> ()); + + runLua ("result = Int (1) <= Int (2)"); + ASSERT_EQ (true, result <bool> ()); + + runLua ("result = Int (2) <= Int (1)"); + ASSERT_EQ (false, result <bool> ()); +} + +TEST_F (ClassMetaMethods, __add) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__add", &Int::operator+) + .endClass (); + + runLua ("result = Int (1) + Int (2)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (3, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __sub) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__sub", &Int::operator-) + .endClass (); + + runLua ("result = Int (1) - Int (2)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (-1, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __mul) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__mul", &Int::operator*) + .endClass (); + + runLua ("result = Int (-2) * Int (-5)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (10, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __div) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__div", &Int::operator/) + .endClass (); + + runLua ("result = Int (10) / Int (2)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (5, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __mod) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__mod", &Int::operator%) + .endClass (); + + runLua ("result = Int (7) % Int (2)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (1, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __pow) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__pow", &Int::operator-) + .endClass (); + + runLua ("result = Int (5) ^ Int (2)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (3, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __unm) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__unm", &Int::negate) + .endClass (); + + runLua ("result = -Int (-3)"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ (3, result <Int> ().data); +} + +TEST_F (ClassMetaMethods, __concat) +{ + typedef Class <std::string, EmptyBase> String; + + luabridge::getGlobalNamespace (L) + .beginClass <String> ("String") + .addConstructor <void (*) (std::string)> () + .addFunction ("__concat", &String::operator+) + .endClass (); + + ASSERT_THROW (runLua ("result = String ('a') + String ('b')"), std::exception); + + runLua ("result = String ('ab') .. String ('cd')"); + ASSERT_TRUE (result ().isUserdata ()); + ASSERT_EQ ("abcd", result <String> ().data); +} + +TEST_F (ClassMetaMethods, __len) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addConstructor <void (*) (int)> () + .addFunction ("__len", &Int::len) + .endClass (); + + runLua ("result = #Int (1)"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = #Int (5)"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (5, result <int> ()); +} + +namespace { + +struct Table +{ + int index (const std::string& key) + { + return map.at (key); + } + + void newIndex (const std::string& key, int value) + { + map.emplace (key, value); + } + + std::map <std::string, int> map; +}; + +} // namespace + +TEST_F (ClassMetaMethods, __index) +{ + luabridge::getGlobalNamespace (L) + .beginClass <Table> ("Table") + .addFunction ("__index", &Table::index) + .endClass (); + + Table t {{{"a", 1}, {"b", 2}}}; + + luabridge::setGlobal (L, &t, "t"); + + runLua ("result = t.a"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (1, result <int> ()); + + runLua ("result = t.b"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (2, result <int> ()); + + ASSERT_THROW (runLua ("result = t.c"), std::exception); // at ("c") throws +} + +TEST_F (ClassMetaMethods, __newindex) +{ + typedef Class <int, EmptyBase> Int; + + luabridge::getGlobalNamespace (L) + .beginClass <Table> ("Table") + .addFunction ("__newindex", &Table::newIndex) + .endClass (); + + Table t; + + luabridge::setGlobal (L, &t, "t"); + + runLua ("t.a = 1\n" + "t ['b'] = 2"); + + ASSERT_EQ ((std::map <std::string, int> {{"a", 1}, {"b", 2}}), t.map); +} + +TEST_F (ClassMetaMethods, __gcForbidden) +{ + typedef Class <int, EmptyBase> Int; + + ASSERT_THROW ( + luabridge::getGlobalNamespace (L) + .beginClass <Int> ("Int") + .addFunction ("__gc", &Int::method) + .endClass (), + std::exception); +} + +TEST_F (ClassTests, EnclosedClassProperties) +{ + typedef Class <int, EmptyBase> Inner; + typedef Class <Inner, EmptyBase> Outer; + + luabridge::getGlobalNamespace (L) + .beginClass <Inner> ("Inner") + .addProperty ("data", &Inner::data) + .endClass () + .beginClass <Outer> ("Outer") + .addProperty ("data", &Outer::data) + .endClass (); + + Outer outer (Inner (0)); + luabridge::setGlobal (L, &outer, "outer"); + + outer.data.data = 1; + runLua ("outer.data.data = 10"); + ASSERT_EQ (10, outer.data.data); + + runLua ("result = outer.data.data"); + ASSERT_EQ (10, result <int> ()); +} diff --git a/lib/LuaBridge/Tests/Source/IssueTests.cpp b/lib/LuaBridge/Tests/Source/IssueTests.cpp new file mode 100644 index 0000000..f6c6514 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/IssueTests.cpp @@ -0,0 +1,145 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +struct IssueTests : TestBase +{ +}; + +struct AbstractClass +{ + virtual int sum (int a, int b) = 0; +}; + +struct ConcreteClass : AbstractClass +{ + int sum (int a, int b) override + { + return a + b; + } + + static AbstractClass& get () + { + static ConcreteClass instance; + return instance; + } +}; + +TEST_F (IssueTests, Issue87) +{ + luabridge::getGlobalNamespace (L) + .beginClass <AbstractClass> ("Class") + .addFunction ("sum", &AbstractClass::sum) + .endClass () + .addFunction ("getAbstractClass", &ConcreteClass::get); + + runLua ("result = getAbstractClass ():sum (1, 2)"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (3, result <int> ()); +} + +TEST_F (IssueTests, Issue121) +{ + runLua (R"( + first = { + second = { + actual = "data" + } + } + )"); + auto first = luabridge::getGlobal (L, "first"); + ASSERT_TRUE (first.isTable ()); + ASSERT_EQ (0, first.length ()); + ASSERT_TRUE (first ["second"].isTable ()); + ASSERT_EQ (0, first ["second"].length ()); +} + +void pushArgs (lua_State*) +{ +} + +template <class Arg, class... Args> +void pushArgs (lua_State* L, Arg arg, Args... args) +{ + luabridge::Stack <Arg>::push (L, arg); + pushArgs (L, args...); +} + +template <class... Args> +std::vector <luabridge::LuaRef> callFunction (const luabridge::LuaRef& function, Args... args) +{ + assert (function.isFunction ()); + + lua_State* L = function.state (); + int originalTop = lua_gettop (L); + function.push (L); + pushArgs (L, args...); + + luabridge::LuaException::pcall (L, sizeof... (args), LUA_MULTRET); + + std::vector <luabridge::LuaRef> results; + int top = lua_gettop (L); + results.reserve (top - originalTop); + for (int i = originalTop + 1; i <= top; ++i) + { + results.push_back (luabridge::LuaRef::fromStack (L, i)); + } + return results; +} + +TEST_F (IssueTests, Issue160) +{ + runLua ( + "function isConnected (arg1, arg2) " + " return 1, 'srvname', 'ip:10.0.0.1', arg1, arg2 " + "end"); + + luabridge::LuaRef f_isConnected = luabridge::getGlobal (L, "isConnected"); + + auto v = callFunction (f_isConnected, 2, "abc"); + ASSERT_EQ (5u, v.size ()); + ASSERT_EQ (1, v [0].cast <int> ()); + ASSERT_EQ ("srvname", v [1].cast <std::string> ()); + ASSERT_EQ ("ip:10.0.0.1", v [2].cast <std::string> ()); + ASSERT_EQ (2, v [3].cast <int> ()); + ASSERT_EQ ("abc", v [4].cast <std::string> ()); +} + +struct Vector +{ + float getX () const + { + return x; + } + + float x = 0; +}; + +struct WideVector : Vector +{ + WideVector (float, float, float, float w) + { + x = w; + } +}; + +TEST_F (IssueTests, Issue178) +{ + luabridge::getGlobalNamespace (L) + .beginClass <Vector> ("Vector") + .addFunction ("getX", &Vector::getX) + .addProperty ("X", &Vector::getX) + .addData ("x", &Vector::x, true) + .endClass () + .deriveClass <WideVector, Vector> ("WideVector") + .addConstructor <void (*) (float, float, float, float)> () + .endClass (); + + runLua ("result = WideVector (0, 1, 2, 3).x"); + + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (3.f, result <float> ()); +} diff --git a/lib/LuaBridge/Tests/Source/IteratorTests.cpp b/lib/LuaBridge/Tests/Source/IteratorTests.cpp new file mode 100644 index 0000000..d62c068 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/IteratorTests.cpp @@ -0,0 +1,96 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2018, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include "LuaBridge/detail/Iterator.h" + +struct IteratorTests : TestBase +{ +}; + +TEST_F (IteratorTests, DictionaryIteration) +{ + runLua ( + "result = {" + " bool = true," + " int = 5," + " c = 3.14," + " [true] = 'D'," + " [8] = 'abc'," + " fn = function (i)" + " result = i + 1" + " end" + "}"); + + std::map <luabridge::LuaRef, luabridge::LuaRef> expected { + {{L, "bool"}, {L, true}}, + {{L, "int"}, {L, 5}}, + {{L, 'c'}, {L, 3.14}}, + {{L, true}, {L, 'D'}}, + {{L, 8}, {L, "abc"}}, + {{L, "fn"}, {L, result () ["fn"]}}, + }; + + std::map <luabridge::LuaRef, luabridge::LuaRef> actual; + + for (luabridge::Iterator iterator (result ()); !iterator.isNil (); ++iterator) + { + actual.emplace(iterator.key (), iterator.value ()); + } + + ASSERT_EQ (expected, actual); + + actual.clear (); + + for (auto&& pair : pairs (result ())) + { + actual.emplace (pair.first, pair.second); + } + + ASSERT_EQ (expected, actual); +} + +TEST_F (IteratorTests, SequenceIteration) +{ + runLua ( + "result = {" + " true," + " 5," + " 3.14," + " 'D'," + " 'abc'," + " function (i)" + " result = i + 1" + " end" + "}"); + + std::map <luabridge::LuaRef, luabridge::LuaRef> expected { + {{L, 1}, {L, true}}, + {{L, 2}, {L, 5}}, + {{L, 3}, {L, 3.14}}, + {{L, 4}, {L, 'D'}}, + {{L, 5}, {L, "abc"}}, + {{L, 6}, {L, result () [6]}}, + }; + + std::map <luabridge::LuaRef, luabridge::LuaRef> actual; + + for (luabridge::Iterator iterator (result ()); !iterator.isNil (); ++iterator) + { + actual.emplace (iterator.key (), iterator.value ()); + } + + ASSERT_EQ (expected, actual); + + actual.clear (); + + for (auto&& pair : pairs (result ())) + { + actual.emplace (pair.first, pair.second); + } + + ASSERT_EQ (expected, actual); +} diff --git a/lib/LuaBridge/Tests/Source/LegacyTests.cpp b/lib/LuaBridge/Tests/Source/LegacyTests.cpp new file mode 100644 index 0000000..861a418 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/LegacyTests.cpp @@ -0,0 +1,354 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com> +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +// A set of tests of different types' communication with Lua + +#include "TestBase.h" + +#include "LuaBridge/RefCountedPtr.h" + +#include "JuceLibraryCode/BinaryData.h" + +#include <cstring> +#include <iostream> +#include <memory> +#include <string> + +namespace LuaBridgeTests { + +using namespace std; +using namespace luabridge; + +//============================================================================== + +/* + * Test classes + */ + +bool g_success = true; + +bool testSucceeded () +{ + bool b = g_success; + g_success = false; + return b; +} + +typedef int fn_type; +enum { + FN_CTOR, + FN_DTOR, + FN_STATIC, + FN_VIRTUAL, + FN_PROPGET, + FN_PROPSET, + FN_STATIC_PROPGET, + FN_STATIC_PROPSET, + FN_OPERATOR, + NUM_FN_TYPES +}; + +struct fn_called { + bool called [NUM_FN_TYPES]; + fn_called () { memset(called, 0, NUM_FN_TYPES * sizeof(bool)); } +}; + +fn_called A_functions, B_functions; + +bool testAFnCalled (fn_type f) +{ + bool b = A_functions.called[f]; + A_functions.called [f] = false; + return b; +} + +bool testBFnCalled (fn_type f) +{ + bool b = B_functions.called[f]; + B_functions.called [f] = false; + return b; +} + +class A +{ +protected: + string name; + mutable bool success; +public: + A (string const& name_) : name (name_), success (false), testProp (47) + { + A_functions.called [FN_CTOR] = true; + } + virtual ~A () + { + A_functions.called [FN_DTOR] = true; + } + + virtual void testVirtual () + { + A_functions.called [FN_VIRTUAL] = true; + } + + const char * getName () const + { + return name.c_str(); + } + + void setSuccess () const + { + success = true; + } + + bool testSucceeded () const + { + bool b = success; + success = false; + return b; + } + + static void testStatic () + { + A_functions.called [FN_STATIC] = true; + } + + int testProp; + int testPropGet () const + { + A_functions.called [FN_PROPGET] = true; + return testProp; + } + void testPropSet (int x) + { + A_functions.called [FN_PROPSET] = true; + testProp = x; + } + + static int testStaticProp; + static int testStaticPropGet () + { + A_functions.called [FN_STATIC_PROPGET] = true; + return testStaticProp; + } + static void testStaticPropSet (int x) + { + A_functions.called [FN_STATIC_PROPSET] = true; + testStaticProp = x; + } + + RefCountedPtr <A> operator + (A const& other) + { + A_functions.called [FN_OPERATOR] = true; + return new A (name + " + " + other.name); + } +}; + +int A::testStaticProp = 47; + +class B: public A +{ +public: + explicit B (string const& name_) : A (name_) + { + B_functions.called [FN_CTOR] = true; + } + + virtual ~B () + { + B_functions.called [FN_DTOR] = true; + } + + virtual void testVirtual () + { + B_functions.called [FN_VIRTUAL] = true; + } + + static void testStatic2 () + { + B_functions.called [FN_STATIC] = true; + } + +}; + +/* + * Test functions + */ + +int testRetInt () +{ + return 47; +} + +float testRetFloat () +{ + return 47.0f; +} + +char const* testRetConstCharPtr () +{ + return "Hello, world"; +} + +string testRetStdString () +{ + static string ret ("Hello, world"); + return ret; +} + +void testParamInt (int a) +{ + g_success = (a == 47); +} + +void testParamBool (bool b) +{ + g_success = b; +} + +void testParamFloat (float f) +{ + g_success = (f == 47.0f); +} + +void testParamConstCharPtr (char const* str) +{ + g_success = !strcmp (str, "Hello, world"); +} + +void testParamStdString (string str) +{ + g_success = !strcmp (str.c_str(), "Hello, world"); +} + +void testParamStdStringRef (const string &str) +{ + g_success = !strcmp (str.c_str(), "Hello, world"); +} + +void testParamAPtr (A * a) +{ + a->setSuccess(); +} + +void testParamAPtrConst (A * const a) +{ + a->setSuccess(); +} + +void testParamConstAPtr (const A * a) +{ + a->setSuccess(); +} + +void testParamSharedPtrA (RefCountedPtr <A> a) +{ + a->setSuccess(); +} + +RefCountedPtr <A> testRetSharedPtrA () +{ + static RefCountedPtr <A> sp_A (new A("from C")); + return sp_A; +} + +RefCountedPtr <A const> testRetSharedPtrConstA () +{ + static RefCountedPtr <A> sp_A (new A("const A")); + return sp_A; +} + +// add our own functions and classes to a Lua environment +void addToState (lua_State *L) +{ + getGlobalNamespace (L) + .addFunction ("testSucceeded", &testSucceeded) + .addFunction ("testAFnCalled", &testAFnCalled) + .addFunction ("testBFnCalled", &testBFnCalled) + .addFunction ("testRetInt", &testRetInt) + .addFunction ("testRetFloat", &testRetFloat) + .addFunction ("testRetConstCharPtr", &testRetConstCharPtr) + .addFunction ("testRetStdString", &testRetStdString) + .addFunction ("testParamInt", &testParamInt) + .addFunction ("testParamBool", &testParamBool) + .addFunction ("testParamFloat", &testParamFloat) + .addFunction ("testParamConstCharPtr", &testParamConstCharPtr) + .addFunction ("testParamStdString", &testParamStdString) + .addFunction ("testParamStdStringRef", &testParamStdStringRef) + .beginClass <A> ("A") + .addConstructor <void (*) (const string &), RefCountedPtr <A> > () + .addFunction ("testVirtual", &A::testVirtual) + .addFunction ("getName", &A::getName) + .addFunction ("testSucceeded", &A::testSucceeded) + .addFunction ("__add", &A::operator+) + .addData ("testProp", &A::testProp) + .addProperty ("testProp2", &A::testPropGet, &A::testPropSet) + .addStaticFunction ("testStatic", &A::testStatic) + .addStaticData ("testStaticProp", &A::testStaticProp) + .addStaticProperty ("testStaticProp2", &A::testStaticPropGet, &A::testStaticPropSet) + .endClass () + .deriveClass <B, A> ("B") + .addConstructor <void (*) (const string &), RefCountedPtr <B> > () + .addStaticFunction ("testStatic2", &B::testStatic2) + .endClass () + .addFunction ("testParamAPtr", &testParamAPtr) + .addFunction ("testParamAPtrConst", &testParamAPtrConst) + .addFunction ("testParamConstAPtr", &testParamConstAPtr) + .addFunction ("testParamSharedPtrA", &testParamSharedPtrA) + .addFunction ("testRetSharedPtrA", &testRetSharedPtrA) + .addFunction ("testRetSharedPtrConstA", &testRetSharedPtrConstA) + ; +} + +void resetTests () +{ + g_success = true; + A::testStaticProp = 47; +} + +void printValue (lua_State* L, int index) +{ + int type = lua_type (L, index); + switch (type) + { + case LUA_TBOOLEAN: + std::cerr << std::boolalpha << (lua_toboolean (L, index) != 0); + break; + case LUA_TSTRING: + std::cerr << lua_tostring (L, index); + break; + case LUA_TNUMBER: + std::cerr << lua_tonumber (L, index); + break; + case LUA_TTABLE: + case LUA_TTHREAD: + case LUA_TFUNCTION: + std::cerr << lua_topointer (L, index); + break; + } + std::cerr << ": " << lua_typename (L, type) << " (" << type << ")" << std::endl; +} + +} // namespace LuaBridgeTests + +struct LegacyTests : TestBase +{ +}; + +TEST_F (LegacyTests, AllTests) +{ + LuaBridgeTests::addToState (L); + + // Execute lua files in order + if (luaL_loadstring (L, BinaryData::Tests_lua) != 0) + { + // compile-time error + FAIL () << lua_tostring (L, -1); + } + if (lua_pcall (L, 0, 0, -2) != 0) + { + // runtime error + FAIL () << lua_tostring (L, -1); + } +} diff --git a/lib/LuaBridge/Tests/Source/ListTests.cpp b/lib/LuaBridge/Tests/Source/ListTests.cpp new file mode 100644 index 0000000..5376116 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/ListTests.cpp @@ -0,0 +1,77 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + + +#include "TestBase.h" +#include "TestTypes.h" + +#include "LuaBridge/List.h" + +#include <list> + + +namespace { + +template <class T> + +std::list <T> toList (const std::vector <T>& vector) +{ + return {vector.begin (), vector.end ()}; +} + +} // namespace + + +template <class T> +struct ListTest : TestBase +{ +}; + +TYPED_TEST_CASE_P (ListTest); + +TYPED_TEST_P (ListTest, LuaRef) +{ + using Traits = TypeTraits <TypeParam>; + + this->runLua ("result = {" + Traits::list () + "}"); + + std::list <TypeParam> expected = toList (Traits::values ()); + std::list <TypeParam> actual = this->result (); + ASSERT_EQ (expected, actual); +} + +REGISTER_TYPED_TEST_CASE_P (ListTest, LuaRef); + +INSTANTIATE_TYPED_TEST_CASE_P(ListTest, ListTest, TestTypes); + + +struct ListTests : TestBase +{ +}; + + +TEST_F (ListTests, PassToFunction) +{ + runLua ( + "function foo (list) " + " result = list " + "end"); + + auto foo = luabridge::getGlobal (L, "foo"); + + resetResult (); + + std::list <int> lvalue {10, 20, 30}; + foo (lvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (lvalue, result <std::list<int>> ()); + + resetResult (); + + const std::list <int> constLvalue = lvalue; + foo (constLvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (lvalue, result <std::list<int>> ()); +} diff --git a/lib/LuaBridge/Tests/Source/LuaRefTests.cpp b/lib/LuaBridge/Tests/Source/LuaRefTests.cpp new file mode 100644 index 0000000..eaffe0c --- /dev/null +++ b/lib/LuaBridge/Tests/Source/LuaRefTests.cpp @@ -0,0 +1,304 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com> +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include <sstream> + + +struct LuaRefTests : TestBase +{ +}; + +TEST_F (LuaRefTests, ValueAccess) +{ + runLua ("result = true"); + ASSERT_TRUE (result ().isBool ()); + ASSERT_TRUE (result <bool> ()); + + runLua ("result = 7"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (7u, result <unsigned char> ()); + ASSERT_EQ (7, result <short> ()); + ASSERT_EQ (7u, result <unsigned short> ()); + ASSERT_EQ (7, result <int> ()); + ASSERT_EQ (7u, result <unsigned int> ()); + ASSERT_EQ (7, result <long> ()); + ASSERT_EQ (7u, result <unsigned long> ()); + ASSERT_EQ (7, result <long long> ()); + ASSERT_EQ (7u, result <unsigned long long> ()); + + runLua ("result = 3.14"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_FLOAT_EQ (3.14f, result <float> ()); + ASSERT_DOUBLE_EQ (3.14, result <double> ()); + + runLua ("result = 'D'"); + ASSERT_TRUE (result ().isString ()); + ASSERT_EQ ('D', result <char> ()); + ASSERT_EQ ("D", result <std::string> ()); + ASSERT_STREQ ("D", result <const char*> ()); + + runLua ("result = 'abc'"); + ASSERT_TRUE (result ().isString ()); + ASSERT_EQ ("abc", result <std::string> ()); + ASSERT_STREQ ("abc", result <char const*> ()); + + runLua ("result = function (i) " + " result = i + 1 " + " return i " + "end"); + ASSERT_TRUE (result ().isFunction ()); + auto fnResult = result () (41); // Replaces result variable + ASSERT_TRUE (fnResult.isNumber ()); + ASSERT_EQ (41, fnResult.cast <int> ()); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (42, result <int> ()); +} + +TEST_F (LuaRefTests, DictionaryRead) +{ + runLua ( + "result = {" + " bool = true," + " int = 5," + " c = 3.14," + " [true] = 'D'," + " [8] = 'abc'," + " fn = function (i) " + " result = i + 1 " + " return i " + " end" + "}"); + + ASSERT_TRUE (result () ["bool"].isBool ()); + ASSERT_TRUE (result () ["bool"].cast <bool> ()); + + ASSERT_TRUE (result () ["int"].isNumber ()); + ASSERT_EQ (5u, result () ["int"].cast <unsigned char> ()); + ASSERT_EQ (5, result () ["int"].cast <short> ()); + ASSERT_EQ (5u, result () ["int"].cast <unsigned short> ()); + ASSERT_EQ (5, result () ["int"].cast <int> ()); + ASSERT_EQ (5u, result () ["int"].cast <unsigned int> ()); + ASSERT_EQ (5, result () ["int"].cast <long> ()); + ASSERT_EQ (5u, result () ["int"].cast <unsigned long> ()); + ASSERT_EQ (5, result () ["int"].cast <long long> ()); + ASSERT_EQ (5u, result () ["int"].cast <unsigned long long> ()); + + ASSERT_TRUE (result () ['c'].isNumber ()); + ASSERT_FLOAT_EQ (3.14f, result () ['c'].cast <float> ()); + ASSERT_DOUBLE_EQ (3.14, result () ['c'].cast <double> ()); + + ASSERT_TRUE (result () [true].isString ()); + ASSERT_EQ ('D', result () [true].cast <char> ()); + ASSERT_EQ ("D", result () [true].cast <std::string> ()); + ASSERT_STREQ ("D", result () [true].cast <const char*> ()); + + ASSERT_TRUE (result () [8].isString ()); + ASSERT_EQ ("abc", result () [8].cast <std::string> ()); + ASSERT_STREQ ("abc", result () [8].cast <char const*> ()); + + ASSERT_TRUE (result () ["fn"].isFunction ()); + auto fnResult = result () ["fn"] (41); // Replaces result variable + ASSERT_TRUE (fnResult.isNumber ()); + ASSERT_EQ (41, fnResult.cast <int> ()); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (42, result <int> ()); +} + +TEST_F (LuaRefTests, DictionaryWrite) +{ + runLua ("result = {a = 5}"); + ASSERT_TRUE (result () ["a"].isNumber ()); + ASSERT_EQ (5, result () ["a"].cast <int> ()); + + result () ["a"] = 7; + ASSERT_EQ (7, result () ["a"].cast <int> ()); + + runLua ("result = result.a"); + ASSERT_EQ (7, result <int> ()); + + runLua ("result = {a = {b = 1}}"); + ASSERT_EQ (1, result () ["a"] ["b"].cast <int> ()); + + result () ["a"] ["b"] = 2; + ASSERT_EQ (2, result () ["a"] ["b"].cast <int> ()); +} + +struct Class +{ +}; + +TEST_F (LuaRefTests, Comparison) +{ + runLua ( + "function foo () end " + "local m = {} " + "m.__eq = function (l, r) return l.a == r.a end " + "m.__lt = function (l, r) return l.a < r.a end " + "m.__le = function (l, r) return l.a <= r.a end " + "t1 = {a = 1} setmetatable (t1, m) " + "t2 = {a = 2} setmetatable (t2, m) " + "t3 = {a = 1} setmetatable (t3, m) " + "t4 = {a = 2} " + ); + + luabridge::getGlobalNamespace (L) + .beginClass <Class> ("Class") + .endClass (); + + luabridge::LuaRef nil (L, luabridge::Nil ()); + luabridge::LuaRef boolFalse (L, false); + luabridge::LuaRef boolTrue (L, true); + luabridge::LuaRef minus5 (L, -5); + luabridge::LuaRef numPi (L, 3.14); + luabridge::LuaRef stringA (L, 'a'); + luabridge::LuaRef stringAB (L, "ab"); + luabridge::LuaRef t1 = luabridge::getGlobal (L, "t1"); + luabridge::LuaRef t2 = luabridge::getGlobal (L, "t2"); + luabridge::LuaRef t3 = luabridge::getGlobal (L, "t3"); + luabridge::LuaRef t4 = luabridge::getGlobal (L, "t4"); + + ASSERT_TRUE (nil == nil); + + ASSERT_TRUE (nil < boolFalse); + + ASSERT_TRUE (boolFalse == boolFalse); + ASSERT_TRUE (boolTrue == boolTrue); + + ASSERT_TRUE (boolTrue < minus5); + + ASSERT_TRUE (minus5 == minus5); + ASSERT_FALSE (minus5 == numPi); + ASSERT_TRUE (minus5 < numPi); + ASSERT_TRUE (minus5 <= numPi); + ASSERT_FALSE (minus5 > numPi); + ASSERT_FALSE (minus5 >= numPi); + + ASSERT_TRUE (numPi < stringA); + + ASSERT_TRUE (stringA == stringA); + ASSERT_FALSE (stringA == stringAB); + ASSERT_TRUE (stringA < stringAB); + ASSERT_TRUE (stringA <= stringAB); + ASSERT_FALSE (stringA > stringAB); + ASSERT_FALSE (stringA >= stringAB); + + ASSERT_TRUE (stringA < t1); + + ASSERT_TRUE (t1 == t1); + ASSERT_FALSE (t1 == t2); + ASSERT_TRUE (t1 == t3); + ASSERT_FALSE (t1.rawequal (t3)); + ASSERT_FALSE (t1 == t4); + ASSERT_TRUE (t2 == t2); + ASSERT_FALSE (t2 == t3); + ASSERT_FALSE (t2 == t4); + ASSERT_TRUE (t3 == t3); + ASSERT_FALSE (t3 == t4); + + ASSERT_FALSE (t1 < t1); + ASSERT_TRUE (t1 < t2); + ASSERT_FALSE (t1 < t3); + ASSERT_FALSE (t2 < t3); + + ASSERT_TRUE (t1 <= t1); + ASSERT_TRUE (t1 <= t2); + ASSERT_TRUE (t1 <= t3); + ASSERT_FALSE (t2 <= t3); + + ASSERT_FALSE (t1 > t1); + ASSERT_FALSE (t1 > t2); + ASSERT_FALSE (t1 > t3); + ASSERT_TRUE (t2 > t3); + + ASSERT_TRUE (t1 >= t1); + ASSERT_FALSE (t1 >= t2); + ASSERT_TRUE (t1 >= t3); + ASSERT_TRUE (t2 >= t3); +} + +TEST_F (LuaRefTests, Assignment) +{ + runLua ("value = {a = 5}"); + auto value = luabridge::getGlobal (L, "value"); + ASSERT_TRUE (value.isTable ()); + ASSERT_TRUE (value ["a"].isNumber ()); + ASSERT_EQ (5, value ["a"].cast <int> ()); + + value = value ["a"]; + ASSERT_TRUE (value.isNumber ()); + ASSERT_EQ (5, value.cast <int> ()); + + value = value; + ASSERT_EQ (LUA_TNUMBER, value.type ()); + ASSERT_TRUE (value.isNumber ()); + ASSERT_EQ (5, value.cast <int> ()); + + runLua ("t = {a = {b = 5}}"); + auto table = luabridge::getGlobal (L, "t"); + luabridge::LuaRef entry = table ["a"]; + luabridge::LuaRef b1 = entry ["b"]; + luabridge::LuaRef b2 = table ["a"] ["b"]; + ASSERT_TRUE (b1 == b2); +} + +TEST_F (LuaRefTests, Print) +{ + { + runLua ("result = true"); + std::ostringstream stream; + stream << result (); + ASSERT_EQ ("true", stream.str ()); + } + { + runLua ("result = false"); + std::ostringstream stream; + stream << result (); + ASSERT_EQ ("false", stream.str ()); + } + { + runLua ("result = 5"); + std::ostringstream stream; + stream << result (); + ASSERT_EQ ("5", stream.str ()); + } + { + runLua ("result = 'abc'"); + std::ostringstream stream; + stream << result (); + ASSERT_EQ ("\"abc\"", stream.str ()); + } + + runLua ( + "result = {" + " true_ = true," + " false_ = false," + " five = 5," + " abc = 'abc'" + "}"); + { + std::ostringstream stream; + stream << result () ["true_"]; + ASSERT_EQ ("true", stream.str ()); + } + { + std::ostringstream stream; + stream << result () ["false_"]; + ASSERT_EQ ("false", stream.str ()); + } + { + std::ostringstream stream; + stream << result () ["five"]; + ASSERT_EQ ("5", stream.str ()); + } + { + std::ostringstream stream; + stream << result () ["abc"]; + ASSERT_EQ ("\"abc\"", stream.str ()); + } +} diff --git a/lib/LuaBridge/Tests/Source/MapTests.cpp b/lib/LuaBridge/Tests/Source/MapTests.cpp new file mode 100644 index 0000000..37fb814 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/MapTests.cpp @@ -0,0 +1,136 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include "LuaBridge/Map.h" + +#include <map> + + +struct MapTests : TestBase +{ +}; + +TEST_F (MapTests, LuaRef) +{ + { + runLua ("result = {[false] = true, a = 'abc', [1] = 5, [3.14] = -1.1}"); + + using Map = std::map <luabridge::LuaRef, luabridge::LuaRef>; + Map expected { + {luabridge::LuaRef (L, false), luabridge::LuaRef (L, true)}, + {luabridge::LuaRef (L, 'a'), luabridge::LuaRef (L, "abc")}, + {luabridge::LuaRef (L, 1), luabridge::LuaRef (L, 5)}, + {luabridge::LuaRef (L, 3.14), luabridge::LuaRef (L, -1.1)}, + }; + Map actual = result (); + ASSERT_EQ (expected, actual); + ASSERT_EQ (expected, result <Map> ()); + } + + { + runLua ("result = {'a', 'b', 'c'}"); + + using Int2Char = std::map <int, char>; + Int2Char expected {{1, 'a'}, {2, 'b'}, {3, 'c'}}; + Int2Char actual = result (); + ASSERT_EQ (expected, actual); + ASSERT_EQ (expected, result <Int2Char> ()); + } +} + +TEST_F (MapTests, PassToFunction) +{ + runLua ( + "function foo (map) " + " result = map " + "end"); + + auto foo = luabridge::getGlobal (L, "foo"); + using Int2Bool = std::map <int, bool>; + + resetResult (); + + Int2Bool lvalue {{10, false}, {20, true}, {30, true}}; + foo (lvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (lvalue, result <Int2Bool> ()); + + resetResult (); + + const Int2Bool constLvalue = lvalue; + foo (constLvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (constLvalue, result <Int2Bool> ()); +} + +namespace { + +struct Data +{ + /* explicit */ Data (int i) : i (i) {} + + int i; +}; + +bool operator== (const Data& lhs, const Data& rhs) +{ + return lhs.i == rhs.i; +} + +bool operator< (const Data& lhs, const Data& rhs) +{ + return lhs.i < rhs.i; +} + +std::ostream& operator<< (std::ostream& lhs, const Data& rhs) +{ + lhs << "{" << rhs.i << "}"; + return lhs; +} + +std::map <Data, Data> processValues (const std::map <Data, Data>& data) +{ + return data; +} + +std::map <Data, Data> processPointers (const std::map <Data, const Data*>& data) +{ + std::map <Data, Data> result; + for (const auto& item : data) + { + result.emplace (item.first, *item.second); + } + return result; +} + +} // namespace + +TEST_F (MapTests, PassFromLua) +{ + luabridge::getGlobalNamespace (L) + .beginClass <Data> ("Data") + .addConstructor <void (*) (int)> () + .endClass () + .addFunction ("processValues", &processValues) + .addFunction ("processPointers", &processPointers); + + { + resetResult (); + runLua ("result = processValues ({[Data (-1)] = Data (2)})"); + std::map <Data, Data> expected {{Data (-1), Data (2)}}; + const auto actual = result <std::map <Data, Data>> (); + ASSERT_EQ (expected, actual); + } + + { + resetResult (); + runLua ("result = processValues ({[Data (3)] = Data (-4)})"); + std::map <Data, Data> expected {{Data (3), Data (-4)}}; + const auto actual = result <std::map <Data, Data>> (); + ASSERT_EQ (expected, actual); + } +} diff --git a/lib/LuaBridge/Tests/Source/NamespaceTests.cpp b/lib/LuaBridge/Tests/Source/NamespaceTests.cpp new file mode 100644 index 0000000..f27e056 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/NamespaceTests.cpp @@ -0,0 +1,250 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + + +struct NamespaceTests : TestBase +{ + template <class T> + T variable (const std::string& name) + { + runLua ("result = " + name); + return result <T> (); + } +}; + +TEST_F (NamespaceTests, Variables) +{ + int int_ = -10; + auto any = luabridge::newTable (L); + any ["a"] = 1; + + ASSERT_THROW ( + luabridge::getGlobalNamespace (L).addProperty ("int", &int_), + std::logic_error); + + runLua ("result = int"); + ASSERT_TRUE (result ().isNil ()); + + luabridge::getGlobalNamespace (L) + .beginNamespace ("ns") + .addProperty ("int", &int_) + .addProperty ("any", &any) + .endNamespace (); + + ASSERT_EQ (-10, variable <int> ("ns.int")); + ASSERT_EQ (any, variable <luabridge::LuaRef> ("ns.any")); + + runLua ("ns.int = -20"); + ASSERT_EQ (-20, int_); + + runLua ("ns.any = {b = 2}"); + ASSERT_TRUE (any.isTable ()); + ASSERT_TRUE (any ["b"].isNumber ()); + ASSERT_EQ (2, any ["b"].cast <int> ()); +} + +TEST_F (NamespaceTests, ReadOnlyVariables) +{ + int int_ = -10; + auto any = luabridge::newTable (L); + any ["a"] = 1; + + ASSERT_THROW ( + luabridge::getGlobalNamespace (L).addProperty ("int", &int_), + std::logic_error); + + runLua ("result = int"); + ASSERT_TRUE (result ().isNil ()); + + luabridge::getGlobalNamespace (L) + .beginNamespace ("ns") + .addProperty ("int", &int_, false) + .addProperty ("any", &any, false) + .endNamespace (); + + ASSERT_EQ (-10, variable <int> ("ns.int")); + ASSERT_EQ (any, variable <luabridge::LuaRef> ("ns.any")); + + ASSERT_THROW (runLua ("ns.int = -20"), std::runtime_error); + ASSERT_EQ (-10, variable <int> ("ns.int")); + + ASSERT_THROW (runLua ("ns.any = {b = 2}"), std::runtime_error); + ASSERT_EQ (any, variable <luabridge::LuaRef> ("ns.any")); +} + +namespace { + +template <class T> +struct Property +{ + static T value; +}; + +template <class T> +T Property <T>::value; + +template <class T> +void setProperty (const T& value) +{ + Property <T>::value = value; +} + +template <class T> +const T& getProperty () +{ + return Property <T>::value; +} + +} // namespace + +TEST_F (NamespaceTests, Properties) +{ + setProperty <int> (-10); + + ASSERT_THROW ( + luabridge::getGlobalNamespace (L) + .addProperty ("int", &getProperty <int>, &setProperty <int>), + std::logic_error); + + runLua ("result = int"); + ASSERT_TRUE (result ().isNil ()); + + luabridge::getGlobalNamespace (L) + .beginNamespace ("ns") + .addProperty ("int", &getProperty <int>, &setProperty <int>) + .endNamespace (); + + ASSERT_EQ (-10, variable <int> ("ns.int")); + + runLua ("ns.int = -20"); + ASSERT_EQ (-20, getProperty <int> ()); +} + +TEST_F (NamespaceTests, ReadOnlyProperties) +{ + setProperty <int> (-10); + + ASSERT_THROW ( + luabridge::getGlobalNamespace (L) + .addProperty ("int", &getProperty <int>), + std::logic_error); + + runLua ("result = int"); + ASSERT_TRUE (result ().isNil ()); + + luabridge::getGlobalNamespace (L) + .beginNamespace ("ns") + .addProperty ("int", &getProperty <int>) + .endNamespace (); + + ASSERT_EQ (-10, variable <int> ("ns.int")); + + ASSERT_THROW ( + runLua ("ns.int = -20"), + std::runtime_error); + ASSERT_EQ (-10, getProperty <int> ()); +} + +namespace { +struct Class {}; +} + +TEST_F (NamespaceTests, LuaStackIntegrity) +{ + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... + + { + auto ns2 = luabridge::getGlobalNamespace (L) + .beginNamespace ("namespace") + .beginNamespace ("ns2"); + + ASSERT_EQ (4, lua_gettop (L)); // Stack: ..., global namespace table (gns), namespace table (ns), ns2 + + ns2.endNamespace (); // Stack: ... + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... + } + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... + + { + auto globalNs = luabridge::getGlobalNamespace (L); + ASSERT_EQ (2, lua_gettop (L)); // Stack: ..., gns + + { + auto ns = luabridge::getGlobalNamespace (L) + .beginNamespace ("namespace"); + // both globalNs an ns are active + ASSERT_EQ (4, lua_gettop (L)); // Stack: ..., gns, gns, ns + } + ASSERT_EQ (2, lua_gettop (L)); // Stack: ..., gns + + { + auto ns = globalNs + .beginNamespace ("namespace"); + // globalNs became inactive + ASSERT_EQ (3, lua_gettop (L)); // Stack: ..., gns, ns + } + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... + + ASSERT_THROW (globalNs.beginNamespace ("namespace"), std::exception); + + ASSERT_THROW (globalNs.beginClass <Class> ("Class"), std::exception); + } + + { + auto globalNs = luabridge::getGlobalNamespace (L) + .beginNamespace ("namespace") + .endNamespace (); + // globalNs is active + ASSERT_EQ (2, lua_gettop (L)); // Stack: ..., gns + } + ASSERT_EQ (1, lua_gettop (L)); // StacK: ... + + { + auto cls = luabridge::getGlobalNamespace (L) + .beginNamespace ("namespace") + .beginClass <Class> ("Class"); + ASSERT_EQ (6, lua_gettop (L)); // Stack: ..., gns, ns, const table, class table, static table + { + auto ns = cls.endClass (); + ASSERT_EQ (3, lua_gettop (L)); // Stack: ..., gns, ns + } + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... + } + ASSERT_EQ (1, lua_gettop (L)); // StacK: ... + + // Test class continuation + { + auto cls = luabridge::getGlobalNamespace (L) + .beginNamespace ("namespace") + .beginClass <Class> ("Class"); + ASSERT_EQ (6, lua_gettop (L)); // Stack: ..., gns, ns, const table, class table, static table + } + ASSERT_EQ (1, lua_gettop (L)); // Stack: ... +} + +#ifdef _M_IX86 // Windows 32bit only + +namespace { + +int __stdcall StdCall (int i) +{ + return i + 10; +} + +} // namespace + +TEST_F (NamespaceTests, StdCallFunctions) +{ + luabridge::getGlobalNamespace (L) + .addFunction ("StdCall", &StdCall); + + runLua ("result = StdCall (2)"); + ASSERT_TRUE (result ().isNumber ()); + ASSERT_EQ (12, result <int> ()); +} + +#endif // _M_IX86 diff --git a/lib/LuaBridge/Tests/Source/PerformanceTests.cpp b/lib/LuaBridge/Tests/Source/PerformanceTests.cpp new file mode 100644 index 0000000..bb074ac --- /dev/null +++ b/lib/LuaBridge/Tests/Source/PerformanceTests.cpp @@ -0,0 +1,165 @@ +//============================================================================== +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com> +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include "JuceLibraryCode/BinaryData.h" + +/** + Command line version of LuaBridge test suite. +*/ + +#include <cstdio> +#include <iostream> +#include <iomanip> +#include <string> +#include <vector> +#include <ctime> + +using namespace std; +using namespace luabridge; + +//------------------------------------------------------------------------------ +/** + Simple stopwatch for measuring elapsed time. +*/ +class Stopwatch +{ +private: + clock_t m_start; + +public: + Stopwatch () + { + start (); + } + + void start () + { + m_start = clock (); + } + + double getElapsedSeconds () + { + clock_t now; + + now = clock (); + + return (double (now - m_start)) / CLOCKS_PER_SEC; + } +}; + +//------------------------------------------------------------------------------ +/** + Classes used for performance tests. +*/ + +struct A +{ + A () : data (0), prop (0) + { + } + + void mf1 () + { + } + + void mf2 (A*) + { + } + + void mf3 (A&) + { + } + + virtual void vf1 () + { + } + + int data; + + int prop; + int getprop () const + { + return prop; + } + void setprop (int v) + { + prop = v; + } +}; + +//------------------------------------------------------------------------------ + +void addToState (lua_State* L) +{ + getGlobalNamespace (L) + .beginClass <A> ("A") + .addConstructor <void (*)(void)> () + .addFunction ("mf1", &A::mf1) + .addFunction ("mf2", &A::mf2) + .addFunction ("mf3", &A::mf3) + .addFunction ("vf1", &A::vf1) + .addData ("data", &A::data) + .addProperty ("prop", &A::getprop, &A::setprop) + .endClass () + ; +} + +void runTests (lua_State* L) +{ + cout.precision (4); + + int result; + + luaL_dostring (L, "a = A()"); + + int const trials = 5; + + for (int trial = 0; trial < trials; ++trial) + { + result = luaL_loadstring (L, "a:mf1 ()"); + if (result != 0) + lua_error (L); + + int const N = 10000000; + + Stopwatch sw; + + sw.start (); + for (int i = 0; i < N; ++i) + { + lua_pushvalue (L, -1); + lua_call (L, 0, 0); + } + + double const seconds = sw.getElapsedSeconds (); + + cout << "Elapsed time: " << seconds << endl; + } +} + +void runPerformanceTests () +{ + lua_State* L = luaL_newstate (); + luaL_openlibs (L); + + addToState (L); + runTests (L); + + lua_close (L); +} + +struct PerformanceTests : TestBase +{ +}; + +TEST_F (PerformanceTests, AllTests) +{ + addToState (L); + runTests (L); +} diff --git a/lib/LuaBridge/Tests/Source/RefCountedPtrTests.cpp b/lib/LuaBridge/Tests/Source/RefCountedPtrTests.cpp new file mode 100644 index 0000000..4e5f582 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/RefCountedPtrTests.cpp @@ -0,0 +1,110 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include "LuaBridge/RefCountedPtr.h" + + +struct RefCountedPtrTests : TestBase +{ + template <class T> + T variable(const std::string& name) + { + runLua("result = " + name); + return result().cast <T>(); + } +}; + +namespace { + +struct RefCounted : luabridge::RefCountedObject +{ + explicit RefCounted(bool& deleted) + : deleted (deleted) + { + deleted = false; + } + + ~RefCounted() + { + deleted = true; + } + + bool isDeleted () const + { + return deleted; + } + + bool& deleted; +}; + +} // namespace + +TEST_F (RefCountedPtrTests, Operators) +{ + bool deleted1 = false; + auto* raw_ptr1 = new RefCounted (deleted1); + luabridge::RefCountedObjectPtr <RefCounted> ptr1 (raw_ptr1); + + bool deleted2 = false; + auto* raw_ptr2 = new RefCounted (deleted2); + luabridge::RefCountedObjectPtr <RefCounted> ptr2 (raw_ptr2); + + ASSERT_TRUE (raw_ptr1 == ptr1.getObject ()); + ASSERT_TRUE (ptr1.getObject () == raw_ptr1); +} + +TEST_F (RefCountedPtrTests, LastReferenceInLua) +{ + luabridge::getGlobalNamespace (L) + .beginClass <RefCounted> ("Class") + .addProperty ("deleted", &RefCounted::isDeleted) + .endClass (); + + bool deleted = false; + + luabridge::RefCountedObjectPtr <RefCounted> object (new RefCounted (deleted)); + + luabridge::setGlobal (L, object, "object"); + runLua("result = object.deleted"); + ASSERT_EQ (true, result ().isBool ()); + ASSERT_EQ (false, result <bool> ()); + + object = nullptr; + runLua("result = object.deleted"); + ASSERT_EQ(true, result ().isBool ()); + ASSERT_EQ(false, result <bool>()); + ASSERT_EQ(false, deleted); + + runLua ("object = nil"); + lua_gc (L, LUA_GCCOLLECT, 1); + + ASSERT_EQ (true, deleted); +} + +TEST_F (RefCountedPtrTests, LastReferenceInCpp) +{ + luabridge::getGlobalNamespace (L) + .beginClass <RefCounted> ("Class") + .addProperty ("deleted", &RefCounted::isDeleted) + .endClass (); + + bool deleted = false; + + luabridge::RefCountedObjectPtr <RefCounted> object (new RefCounted (deleted)); + + luabridge::setGlobal (L, object, "object"); + runLua("result = object.deleted"); + ASSERT_EQ (true, result ().isBool ()); + ASSERT_EQ (false, result <bool> ()); + + runLua ("object = nil"); + lua_gc (L, LUA_GCCOLLECT, 1); + ASSERT_EQ(false, deleted); + + object = nullptr; + ASSERT_EQ (true, deleted); +} diff --git a/lib/LuaBridge/Tests/Source/TestBase.h b/lib/LuaBridge/Tests/Source/TestBase.h new file mode 100644 index 0000000..c272610 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/TestBase.h @@ -0,0 +1,98 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com> +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +#pragma once + +#include "Lua/LuaLibrary.h" + +#include "LuaBridge/LuaBridge.h" + +#include <gtest/gtest.h> + +#include <stdexcept> + + +// traceback function, adapted from lua.c +// when a runtime error occurs, this will append the call stack to the error message +// +inline int traceback (lua_State* L) +{ + // look up Lua's 'debug.traceback' function + lua_getglobal (L, "debug"); + if (!lua_istable (L, -1)) + { + lua_pop (L, 1); + return 1; + } + lua_getfield (L, -1, "traceback"); + if (!lua_isfunction (L, -1)) + { + lua_pop (L, 2); + return 1; + } + lua_pushvalue (L, 1); /* pass error message */ + lua_pushinteger (L, 2); /* skip this function and traceback */ + lua_call (L, 2, 1); /* call debug.traceback */ + return 1; +} + +/// Base test class. Introduces the global 'result' variable, +/// used for checking of C++ - Lua interoperation. +/// +struct TestBase : public ::testing::Test +{ + lua_State* L = nullptr; + + void SetUp () override + { + L = nullptr; + L = luaL_newstate (); + luaL_openlibs (L); + lua_pushcfunction (L, &traceback); + } + + void TearDown () override + { + if (L != nullptr) + { + lua_close (L); + } + } + + void runLua (const std::string& script) const + { + if (luaL_loadstring (L, script.c_str ()) != 0) + { + throw std::runtime_error (lua_tostring (L, -1)); + } + + if (lua_pcall (L, 0, 0, -2) != 0) + { + throw std::runtime_error (lua_tostring (L, -1)); + } + } + + template <class T = luabridge::LuaRef> + T result () const + { + return luabridge::getGlobal (L, "result").cast <T> (); + } + + void resetResult () const + { + luabridge::setGlobal (L, luabridge::LuaRef (L), "result"); + } + + void printStack () const + { + std::cerr << "===== Stack =====\n"; + for (int i = 1; i <= lua_gettop (L); ++i) + { + std::cerr << "@" << i << " = " << luabridge::LuaRef::fromStack (L, i) << "\n"; + } + } +}; diff --git a/lib/LuaBridge/Tests/Source/TestTypes.h b/lib/LuaBridge/Tests/Source/TestTypes.h new file mode 100644 index 0000000..2efaf45 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/TestTypes.h @@ -0,0 +1,122 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#pragma once + +#include <gtest/gtest.h> + +#include <vector> +#include <string> + + +using TestTypes = ::testing::Types < + bool, + char, + unsigned char, + short, + unsigned short, + int, + unsigned int, + long, + unsigned long, + long long, + unsigned long long, + float, + double +>; + +template <class T> +struct TypeTraits; + +template <> +struct TypeTraits <bool> +{ + static std::vector <bool> values () {return {true, false, true};} + static std::string list () {return "true, false, true";} +}; + +template <> +struct TypeTraits <char> +{ + static std::vector <char> values () {return {'a', 'b', 'c'};} + static std::string list () {return "'a', 'b', 'c'";} +}; + +template <> +struct TypeTraits <unsigned char> +{ + static std::vector <unsigned char> values () {return {1, 2, 3};} + static std::string list () {return "1, 2, 3";} +}; + +template <> +struct TypeTraits <short> +{ + static std::vector <short> values () {return {1, -2, 3};} + static std::string list () {return "1, -2, 3";} +}; + +template <> +struct TypeTraits <unsigned short> +{ + static std::vector <unsigned short> values () {return {1, 2, 3};} + static std::string list () {return "1, 2, 3";} +}; + +template <> +struct TypeTraits <int> +{ + static std::vector <int> values () {return {1, -2, 3};} + static std::string list () {return "1, -2, 3";} +}; + +template <> +struct TypeTraits <unsigned int> +{ + static std::vector <unsigned int> values () {return {1, 2, 3};} + static std::string list () {return "1, 2, 3";} +}; + +template <> +struct TypeTraits <long> +{ + static std::vector <long> values () {return {1, -2, 3};} + static std::string list () {return "1, -2, 3";} +}; + +template <> +struct TypeTraits <unsigned long> +{ + static std::vector <unsigned long> values () {return {1, 2, 3};} + static std::string list () {return "1, 2, 3";} +}; + +template <> +struct TypeTraits <long long> +{ + static std::vector <long long> values () {return {1, -2, 3};} + static std::string list () {return "1, -2, 3";} +}; + +template <> +struct TypeTraits <unsigned long long> +{ + static std::vector <unsigned long long> values () {return {1, 2, 3};} + static std::string list () {return "1, 2, 3";} +}; + +template <> +struct TypeTraits <float> +{ + static std::vector <float> values () {return {1.2f, -2.5f, 3.14f};} + static std::string list () {return "1.2, -2.5, 3.14";} +}; + +template <> +struct TypeTraits <double> +{ + static std::vector <double> values () {return {1.2, -2.5, 3.14};} + static std::string list () {return "1.2, -2.5, 3.14";} +}; diff --git a/lib/LuaBridge/Tests/Source/Tests.cpp b/lib/LuaBridge/Tests/Source/Tests.cpp new file mode 100644 index 0000000..c8c9fc5 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/Tests.cpp @@ -0,0 +1,262 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com> +// Copyright 2007, Nathan Reed +// SPDX-License-Identifier: MIT + +// A set of tests of different types' communication with Lua + +#include "TestBase.h" + +#include <gtest/gtest.h> + +#include <cstring> +#include <iostream> +#include <memory> +#include <string> + +void printValue (lua_State* L, int index) +{ + int type = lua_type (L, index); + switch (type) + { + case LUA_TBOOLEAN: + std::cerr << std::boolalpha << (lua_toboolean (L, index) != 0); + break; + case LUA_TSTRING: + std::cerr << lua_tostring (L, index); + break; + case LUA_TNUMBER: + std::cerr << lua_tonumber (L, index); + break; + case LUA_TTABLE: + case LUA_TTHREAD: + case LUA_TFUNCTION: + std::cerr << lua_topointer (L, index); + break; + } + std::cerr << ": " << lua_typename (L, type) << " (" << type << ")" << std::endl; +} + +struct LuaBridgeTest : TestBase +{ +}; + +template <class T> +T identityCFunction(T value) +{ + return value; +} + +TEST_F (LuaBridgeTest, CFunction) +{ + luabridge::getGlobalNamespace(L) + .addFunction ("boolFn", &identityCFunction <bool>) + .addFunction ("ucharFn", &identityCFunction <unsigned char>) + .addFunction ("shortFn", &identityCFunction <short>) + .addFunction ("ushortFn", &identityCFunction <unsigned short>) + .addFunction ("intFn", &identityCFunction <int>) + .addFunction ("uintFn", &identityCFunction <unsigned int>) + .addFunction ("longFn", &identityCFunction <long>) + .addFunction ("ulongFn", &identityCFunction <unsigned long>) + .addFunction ("longlongFn", &identityCFunction <long long>) + .addFunction ("ulonglongFn", &identityCFunction <unsigned long long>) + .addFunction ("floatFn", &identityCFunction <float>) + .addFunction ("doubleFn", &identityCFunction <double>) + .addFunction ("charFn", &identityCFunction <char>) + .addFunction ("cstringFn", &identityCFunction <const char*>) + .addFunction ("stringFn", &identityCFunction <std::string>) + ; + + { + runLua ("result = ucharFn (255)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (255u, result <unsigned char> ()); + } + + { + runLua ("result = boolFn (false)"); + ASSERT_EQ (true, result ().isBool ()); + ASSERT_EQ (false, result <bool> ()); + } + { + runLua ("result = boolFn (true)"); + ASSERT_EQ (true, result ().isBool ()); + ASSERT_EQ (true, result <bool> ()); + } + + { + runLua ("result = shortFn (-32768)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (-32768, result <int> ()); + } + + { + runLua ("result = ushortFn (32767)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (32767u, result <unsigned int> ()); + } + { + runLua ("result = intFn (-500)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (-500, result <int> ()); + } + + { + runLua ("result = uintFn (42)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (42u, result <unsigned int> ()); + } + + { + runLua ("result = longFn (-8000)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (-8000, result <long> ()); + } + + { + runLua ("result = ulongFn (9000)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (9000u, result <unsigned long> ()); + } + + { + runLua ("result = longlongFn (-8000)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (-8000, result <long long> ()); + } + + { + runLua ("result = ulonglongFn (9000)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_EQ (9000u, result <unsigned long long> ()); + } + + { + runLua ("result = floatFn (3.14)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_FLOAT_EQ (3.14f, result <float> ()); + } + + { + runLua ("result = doubleFn (-12.3)"); + ASSERT_EQ (true, result ().isNumber ()); + ASSERT_DOUBLE_EQ (-12.3, result <double> ()); + } + + { + runLua ("result = charFn ('a')"); + ASSERT_EQ (true, result ().isString ()); + ASSERT_EQ ('a', result <char> ()); + } + + { + runLua ("result = cstringFn ('abc')"); + ASSERT_EQ (true, result ().isString ()); + ASSERT_STREQ ("abc", result <const char*> ()); + } + + { + runLua ("result = stringFn ('lua')"); + ASSERT_EQ (true, result ().isString ()); + ASSERT_EQ ("lua", result <std::string> ()); + } +} + +template <class T> +struct TestClass +{ + TestClass (T data) + : data (data) + , constData (data) + { + } + + T getValue () { return data; } + T* getPtr () { return &data; } + T const* getConstPtr () { return &data; } + T& getRef () { return data; } + T const& getConstRef () { return data; } + T getValueConst () const { return data; } + T* getPtrConst () const { return &data; } + T const* getConstPtrConst () const { return &data; } + T& getRefConst () const { return data; } + T const& getConstRefConst () const { return data; } + + mutable T data; + mutable T constData; +}; + +TEST_F (LuaBridgeTest, ClassFunction) +{ + typedef TestClass <int> Inner; + typedef TestClass <Inner> Outer; + + luabridge::getGlobalNamespace (L) + .beginClass <Inner> ("Inner") + .addConstructor <void (*) (int)> () + .addData ("data", &Inner::data) + .endClass () + .beginClass <Outer> ("Outer") + .addConstructor <void (*) (Inner)> () + .addFunction ("getValue", &Outer::getValue) + .addFunction ("getPtr", &Outer::getPtr) + .addFunction ("getConstPtr", &Outer::getConstPtr) + .addFunction ("getRef", &Outer::getRef) + .addFunction ("getConstRef", &Outer::getConstRef) + .addFunction ("getValueConst", &Outer::getValueConst) + .addFunction ("getPtrConst", &Outer::getPtrConst) + .addFunction ("getConstPtrConst", &Outer::getConstPtrConst) + .addFunction ("getRefConst", &Outer::getRefConst) + .addFunction ("getConstRefConst", &Outer::getConstRefConst) + .endClass () + ; + + Outer outer (Inner (0)); + luabridge::setGlobal (L, &outer, "outer"); + + outer.data.data = 0; + runLua ("outer:getValue ().data = 1"); + ASSERT_EQ (0, outer.data.data); + + outer.data.data = 1; + runLua ("outer:getPtr ().data = 10"); + ASSERT_EQ (10, outer.data.data); + + outer.data.data = 2; + ASSERT_THROW ( + runLua ("outer:getConstPtr ().data = 20"), + std::runtime_error); + + outer.data.data = 3; + runLua ("outer:getRef().data = 30"); + ASSERT_EQ (30, outer.data.data); + + outer.data.data = 4; + ASSERT_THROW ( + runLua ("outer:getConstPtr ().data = 40"), + std::runtime_error); + + outer.data.data = 5; + runLua ("outer:getValueConst ().data = 50"); + ASSERT_EQ (5, outer.data.data); + + outer.data.data = 6; + runLua ("outer:getPtrConst ().data = 60"); + ASSERT_EQ (60, outer.data.data); + + outer.data.data = 7; + ASSERT_THROW ( + runLua ("outer:getConstPtr ().data = 70"), + std::runtime_error); + + outer.data.data = 8; + runLua ("outer:getRef().data = 80"); + ASSERT_EQ (80, outer.data.data); + + outer.data.data = 9; + ASSERT_THROW ( + runLua ("outer:getConstPtr ().data = 90"), + std::runtime_error); +} diff --git a/lib/LuaBridge/Tests/Source/Tests.lua b/lib/LuaBridge/Tests/Source/Tests.lua new file mode 100644 index 0000000..7bc4aa9 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/Tests.lua @@ -0,0 +1,107 @@ +-- test lua script to be run with the luabridge test program + +print("Running LuaBridge tests:"); + +-- enum from C++ +FN_CTOR = 0 +FN_DTOR = 1 +FN_STATIC = 2 +FN_VIRTUAL = 3 +FN_PROPGET = 4 +FN_PROPSET = 5 +FN_STATIC_PROPGET = 6 +FN_STATIC_PROPSET = 7 +FN_OPERATOR = 8 +NUM_FN_TYPES = 9 + +-- function to print contents of a table +function printtable (t) + for k, v in pairs(t) do + if (type(v) == "table") then + print(k .. " =>", "(table)"); + elseif (type(v) == "function") then + print(k .. " =>", "(function)"); + elseif (type(v) == "userdata") then + print(k .. " =>", "(userdata)"); + else + print(k .. " =>", v); + end + end +end + +function assert (expr) + if (not expr) then error("assert failed", 2) end +end + +-- test functions registered from C++ + +assert(testSucceeded()); +assert(testRetInt() == 47); +assert(testRetFloat() == 47.0); +assert(testRetConstCharPtr() == "Hello, world"); +assert(testRetStdString() == "Hello, world"); + +testParamInt(47); assert(testSucceeded()); +testParamBool(true); assert(testSucceeded()); +testParamFloat(47.0); assert(testSucceeded()); +testParamConstCharPtr("Hello, world"); assert(testSucceeded()); +testParamStdString("Hello, world"); assert(testSucceeded()); +testParamStdStringRef("Hello, world"); assert(testSucceeded()); + +-- test static methods of classes registered from C++ + +A.testStatic(); assert(testAFnCalled(FN_STATIC)); +B.testStatic(); assert(testAFnCalled(FN_STATIC)); +B.testStatic2(); assert(testBFnCalled(FN_STATIC)); + +-- test static properties of classes registered from C++ + +assert(A.testStaticProp == 47); +assert(A.testStaticProp2 == 47);assert(testAFnCalled(FN_STATIC_PROPGET)); +A.testStaticProp = 48; assert(A.testStaticProp == 48); +A.testStaticProp2 = 49; assert(testAFnCalled(FN_STATIC_PROPSET) and A.testStaticProp2 == 49); + +-- test classes registered from C++ + +object1 = A("object1"); assert(testAFnCalled(FN_CTOR)); +object1:testVirtual(); assert(testAFnCalled(FN_VIRTUAL)); + +object2 = B("object2"); assert(testAFnCalled(FN_CTOR) and testBFnCalled(FN_CTOR)); +object2:testVirtual(); assert(testBFnCalled(FN_VIRTUAL) and not testAFnCalled(FN_VIRTUAL)); + +-- test functions taking and returning objects + +testParamAPtr(object1); assert(object1:testSucceeded()); +testParamAPtrConst(object1); assert(object1:testSucceeded()); +testParamConstAPtr(object1); assert(object1:testSucceeded()); +testParamSharedPtrA(object1); assert(object1:testSucceeded()); + +testParamAPtr(object2); assert(object2:testSucceeded()); +testParamAPtrConst(object2); assert(object2:testSucceeded()); +testParamConstAPtr(object2); assert(object2:testSucceeded()); +testParamSharedPtrA(object2); assert(object2:testSucceeded()); + +result = testRetSharedPtrA(); assert(result:getName() == "from C"); + +-- test constness + +constA = testRetSharedPtrConstA(); assert(constA:getName() == "const A"); +assert(constA.testVirtual == nil); +testParamConstAPtr(constA); assert(constA:testSucceeded()); +assert(pcall(testParamAPtr, constA) == false, "attempt to call nil value"); + +-- test properties + +assert(object1.testProp == 47); +assert(object1.testProp2 == 47); assert(testAFnCalled(FN_PROPGET)); +assert(object2.testProp == 47); +assert(object2.testProp2 == 47); assert(testAFnCalled(FN_PROPGET)); + +object1.testProp = 48; assert(object1.testProp == 48); +object1.testProp2 = 49; assert(testAFnCalled(FN_PROPSET) and object1.testProp2 == 49); + +-- test operator overload +object1a = object1 + object1; assert(testAFnCalled(FN_OPERATOR)); +assert(object1a:getName() == "object1 + object1"); + +print("All tests succeeded."); diff --git a/lib/LuaBridge/Tests/Source/TestsMain.cpp b/lib/LuaBridge/Tests/Source/TestsMain.cpp new file mode 100644 index 0000000..8b6f716 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/TestsMain.cpp @@ -0,0 +1,19 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2018, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include <gtest/gtest.h> + + +int main (int argc, char** argv) +{ + // Disable performance tests by default + if (argc == 1) + { + testing::GTEST_FLAG (filter) = "-PerformanceTests.AllTests"; + } + + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/lib/LuaBridge/Tests/Source/UnorderedMapTests.cpp b/lib/LuaBridge/Tests/Source/UnorderedMapTests.cpp new file mode 100644 index 0000000..04d159e --- /dev/null +++ b/lib/LuaBridge/Tests/Source/UnorderedMapTests.cpp @@ -0,0 +1,162 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" + +#include "LuaBridge/UnorderedMap.h" + +#include <unordered_map> + + +struct UnorderedMapTests : TestBase +{ +}; + +namespace { + +struct Data +{ + /* explicit */ Data(int i) : i(i) {} + + int i; +}; + +} // namespace + +namespace std { + +template <> +struct hash <Data> +{ + size_t operator() (const Data& value) const noexcept + { + return 0; // Don't care about hash collisions + } +}; + +template <> +struct hash <::luabridge::LuaRef> +{ + size_t operator() (const ::luabridge::LuaRef& value) const + { + return 0; // Don't care about hash collisions + } +}; + +} // namespace std + +TEST_F (UnorderedMapTests, LuaRef) +{ + { + runLua ("result = {[false] = true, a = 'abc', [1] = 5, [3.14] = -1.1}"); + + using Map = std::unordered_map <luabridge::LuaRef, luabridge::LuaRef>; + Map expected { + {luabridge::LuaRef (L, false), luabridge::LuaRef (L, true)}, + {luabridge::LuaRef (L, 'a'), luabridge::LuaRef (L, "abc")}, + {luabridge::LuaRef (L, 1), luabridge::LuaRef (L, 5)}, + {luabridge::LuaRef (L, 3.14), luabridge::LuaRef (L, -1.1)}, + }; + Map actual = result (); + ASSERT_EQ (expected, actual); + ASSERT_EQ (expected, result <Map> ()); + } + + { + runLua ("result = {'a', 'b', 'c'}"); + + using Int2Char = std::unordered_map <int, char>; + Int2Char expected {{1, 'a'}, {2, 'b'}, {3, 'c'}}; + Int2Char actual = result (); + ASSERT_EQ (expected, actual); + ASSERT_EQ (expected, result <Int2Char> ()); + } +} + +TEST_F (UnorderedMapTests, PassToFunction) +{ + runLua ( + "function foo (map) " + " result = map " + "end"); + + auto foo = luabridge::getGlobal (L, "foo"); + using Int2Bool = std::unordered_map <int, bool>; + + resetResult (); + + Int2Bool lvalue {{10, false}, {20, true}, {30, true}}; + foo (lvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (lvalue, result <Int2Bool> ()); + + resetResult (); + + const Int2Bool constLvalue = lvalue; + foo (constLvalue); + ASSERT_TRUE (result ().isTable ()); + ASSERT_EQ (constLvalue, result <Int2Bool> ()); +} + +namespace { + +bool operator== (const Data& lhs, const Data& rhs) +{ + return lhs.i == rhs.i; +} + +bool operator< (const Data& lhs, const Data& rhs) +{ + return lhs.i < rhs.i; +} + +std::ostream& operator<< (std::ostream& lhs, const Data& rhs) +{ + lhs << "{" << rhs.i << "}"; + return lhs; +} + +std::unordered_map <Data, Data> processValues (const std::unordered_map <Data, Data>& data) +{ + return data; +} + +std::unordered_map <Data, Data> processPointers (const std::unordered_map <Data, const Data*>& data) +{ + std::unordered_map <Data, Data> result; + for (const auto& item : data) + { + result.emplace (item.first, *item.second); + } + return result; +} + +} // namespace + +TEST_F (UnorderedMapTests, PassFromLua) +{ + luabridge::getGlobalNamespace (L) + .beginClass <Data> ("Data") + .addConstructor <void (*) (int)> () + .endClass () + .addFunction ("processValues", &processValues) + .addFunction ("processPointers", &processPointers); + + { + resetResult (); + runLua ("result = processValues ({[Data (-1)] = Data (2)})"); + std::unordered_map <Data, Data> expected {{Data (-1), Data (2)}}; + const auto actual = result <std::unordered_map <Data, Data>> (); + ASSERT_EQ (expected, actual); + } + + { + resetResult (); + runLua ("result = processValues ({[Data (3)] = Data (-4)})"); + std::unordered_map <Data, Data> expected {{Data (3), Data (-4)}}; + const auto actual = result <std::unordered_map <Data, Data>> (); + ASSERT_EQ (expected, actual); + } +} diff --git a/lib/LuaBridge/Tests/Source/VectorTests.cpp b/lib/LuaBridge/Tests/Source/VectorTests.cpp new file mode 100644 index 0000000..386e5b3 --- /dev/null +++ b/lib/LuaBridge/Tests/Source/VectorTests.cpp @@ -0,0 +1,99 @@ +// https://github.com/vinniefalco/LuaBridge +// +// Copyright 2019, Dmitry Tarakanov +// SPDX-License-Identifier: MIT + +#include "TestBase.h" +#include "TestTypes.h" + +#include "LuaBridge/Vector.h" + +#include <vector> + +template <class T> +struct VectorTest : TestBase +{ +}; + +TYPED_TEST_CASE_P (VectorTest); + +TYPED_TEST_P (VectorTest, LuaRef) +{ + using Traits = TypeTraits <TypeParam>; + + this->runLua ("result = {" + Traits::list () + "}"); + + std::vector <TypeParam> expected (Traits::values ()); + std::vector <TypeParam> actual = this->result (); + ASSERT_EQ (expected, actual); +} + +REGISTER_TYPED_TEST_CASE_P (VectorTest, LuaRef); + +INSTANTIATE_TYPED_TEST_CASE_P(VectorTest, VectorTest, TestTypes); + + +namespace { + +struct Data +{ + /* explicit */ Data (int i) : i (i) {} + + int i; +}; + +bool operator== (const Data& lhs, const Data& rhs) +{ + return lhs.i == rhs.i; +} + +std::ostream& operator<< (std::ostream& lhs, const Data& rhs) +{ + lhs << "{" << rhs.i << "}"; + return lhs; +} + +std::vector <Data> processValues(const std::vector <Data>& data) +{ + return data; +} + +std::vector <Data> processPointers(const std::vector <const Data*>& data) +{ + std::vector <Data> result; + for (const auto* item : data) + { + result.emplace_back (*item); + } + return result; +} + +} // namespace + +struct VectorTests : TestBase +{ +}; + +TEST_F (VectorTests, PassFromLua) +{ + luabridge::getGlobalNamespace (L) + .beginClass <Data> ("Data") + .addConstructor <void (*) (int)> () + .endClass () + .addFunction ("processValues", &processValues) + .addFunction ("processPointers", &processPointers); + + resetResult (); + runLua ("result = processValues ({Data (-1), Data (2)})"); + + ASSERT_EQ ( + std::vector <Data> ({-1, 2}), + result <std::vector <Data>>()); + + resetResult (); + runLua ("result = processValues ({Data (-3), Data (4)})"); + + ASSERT_EQ( + std::vector <Data> ({-3, 4}), + result <std::vector <Data>> ()); +} |