aboutsummaryrefslogtreecommitdiffstats
path: root/lib/LuaBridge/Tests/Source
diff options
context:
space:
mode:
Diffstat (limited to 'lib/LuaBridge/Tests/Source')
-rw-r--r--lib/LuaBridge/Tests/Source/ClassTests.cpp1665
-rw-r--r--lib/LuaBridge/Tests/Source/IssueTests.cpp145
-rw-r--r--lib/LuaBridge/Tests/Source/IteratorTests.cpp96
-rw-r--r--lib/LuaBridge/Tests/Source/LegacyTests.cpp354
-rw-r--r--lib/LuaBridge/Tests/Source/ListTests.cpp77
-rw-r--r--lib/LuaBridge/Tests/Source/LuaRefTests.cpp304
-rw-r--r--lib/LuaBridge/Tests/Source/MapTests.cpp136
-rw-r--r--lib/LuaBridge/Tests/Source/NamespaceTests.cpp250
-rw-r--r--lib/LuaBridge/Tests/Source/PerformanceTests.cpp165
-rw-r--r--lib/LuaBridge/Tests/Source/RefCountedPtrTests.cpp110
-rw-r--r--lib/LuaBridge/Tests/Source/TestBase.h98
-rw-r--r--lib/LuaBridge/Tests/Source/TestTypes.h122
-rw-r--r--lib/LuaBridge/Tests/Source/Tests.cpp262
-rw-r--r--lib/LuaBridge/Tests/Source/Tests.lua107
-rw-r--r--lib/LuaBridge/Tests/Source/TestsMain.cpp19
-rw-r--r--lib/LuaBridge/Tests/Source/UnorderedMapTests.cpp162
-rw-r--r--lib/LuaBridge/Tests/Source/VectorTests.cpp99
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>> ());
+}