// 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 <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
{
};
const char* const SCRIPT = R"(
-- 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.');
)";
TEST_F(LegacyTests, AllTests)
{
LuaBridgeTests::addToState(L);
// Execute lua files in order
if (luaL_loadstring(L, SCRIPT) != 0)
{
// compile-time error
FAIL() << lua_tostring(L, -1);
}
if (lua_pcall(L, 0, 0, -2) != 0)
{
// runtime error
FAIL() << lua_tostring(L, -1);
}
}