// 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 "LuaBridge/detail/dump.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 "
"table1aMT = {a = 1} setmetatable (table1aMT, m) "
"table1bMT = {a = 1} setmetatable (table1bMT, m) "
"table2aMT = {a = 2} setmetatable (table2aMT, m) "
"table2b = {a = 2} "
"table2c = {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 table1aMT = luabridge::getGlobal(L, "table1aMT");
luabridge::LuaRef table1bMT = luabridge::getGlobal(L, "table1bMT");
luabridge::LuaRef table2aMT = luabridge::getGlobal(L, "table2aMT");
luabridge::LuaRef table2b = luabridge::getGlobal(L, "table2b");
luabridge::LuaRef table2c = luabridge::getGlobal(L, "table2c");
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 < table1aMT);
ASSERT_TRUE(table1aMT == table1aMT);
ASSERT_TRUE(table1aMT == table1bMT);
ASSERT_FALSE(table1aMT == table2aMT);
ASSERT_FALSE(table1aMT.rawequal(table1bMT));
ASSERT_FALSE(table1aMT == table2b);
ASSERT_TRUE(table2aMT == table2aMT);
ASSERT_FALSE(table2aMT == table1bMT);
#if LUABRIDGE_TEST_LUA_VERSION < 503
// Despite its documentation Lua <= 5.2 compares
// the metamethods and ignores them if they differ
ASSERT_FALSE(table2aMT == table2b);
#else
ASSERT_TRUE(table2aMT == table2b);
#endif
ASSERT_TRUE(table1bMT == table1bMT);
ASSERT_FALSE(table1bMT == table2b);
ASSERT_FALSE(table2b == table2c);
ASSERT_FALSE(table1aMT < table1aMT);
ASSERT_TRUE(table1aMT < table2aMT);
ASSERT_FALSE(table1aMT < table1bMT);
ASSERT_FALSE(table2aMT < table1bMT);
ASSERT_TRUE(table1aMT <= table1aMT);
ASSERT_TRUE(table1aMT <= table2aMT);
ASSERT_TRUE(table1aMT <= table1bMT);
ASSERT_FALSE(table2aMT <= table1bMT);
ASSERT_FALSE(table1aMT > table1aMT);
ASSERT_FALSE(table1aMT > table2aMT);
ASSERT_FALSE(table1aMT > table1bMT);
ASSERT_TRUE(table2aMT > table1bMT);
ASSERT_TRUE(table1aMT >= table1aMT);
ASSERT_FALSE(table1aMT >= table2aMT);
ASSERT_TRUE(table1aMT >= table1bMT);
ASSERT_TRUE(table2aMT >= table1bMT);
}
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, IsInstance)
{
struct Base
{
};
struct Derived : Base
{
};
struct Other
{
};
struct Unknown : Base
{
};
luabridge::getGlobalNamespace(L)
.beginClass<Base>("Base")
.addConstructor<void (*)()>()
.endClass()
.deriveClass<Derived, Base>("Derived")
.addConstructor<void (*)()>()
.endClass()
.beginClass<Other>("Other")
.addConstructor<void (*)()>()
.endClass();
runLua("result = Base ()");
ASSERT_TRUE(result().isInstance<Base>());
ASSERT_FALSE(result().isInstance<Derived>());
ASSERT_FALSE(result().isInstance<Other>());
ASSERT_FALSE(result().isInstance<Unknown>());
ASSERT_TRUE(result().isUserdata());
runLua("result = Derived ()");
ASSERT_TRUE(result().isInstance<Base>());
ASSERT_TRUE(result().isInstance<Derived>());
ASSERT_FALSE(result().isInstance<Other>());
ASSERT_FALSE(result().isInstance<Unknown>());
ASSERT_TRUE(result().isUserdata());
runLua("result = Other ()");
ASSERT_FALSE(result().isInstance<Base>());
ASSERT_FALSE(result().isInstance<Derived>());
ASSERT_TRUE(result().isInstance<Other>());
ASSERT_FALSE(result().isInstance<Unknown>());
ASSERT_TRUE(result().isUserdata());
runLua("result = 3.14");
ASSERT_FALSE(result().isInstance<Base>());
ASSERT_FALSE(result().isInstance<Derived>());
ASSERT_FALSE(result().isInstance<Other>());
ASSERT_FALSE(result().isInstance<Unknown>());
ASSERT_FALSE(result().isUserdata());
}
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());
}
}