diff options
author | Andy Belle-Isle <drumsetmonkey@gmail.com> | 2019-08-28 00:57:57 -0400 |
---|---|---|
committer | Andy Belle-Isle <drumsetmonkey@gmail.com> | 2019-08-28 00:57:57 -0400 |
commit | 85fb2bff38b2ef6cb17e86c5f602ee09a365b117 (patch) | |
tree | a8066d33233ec9b6a2b9bb281a1de040ab96be7b /lib/LuaBridge/Tests/Source/ClassTests.cpp | |
parent | 787393dd86d6c37b5680847dd4eef14406a86687 (diff) |
Added LuaBridge support
Diffstat (limited to 'lib/LuaBridge/Tests/Source/ClassTests.cpp')
-rw-r--r-- | lib/LuaBridge/Tests/Source/ClassTests.cpp | 1665 |
1 files changed, 1665 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> ()); +} |