// 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);
}