// https://github.com/vinniefalco/LuaBridge
 
// Copyright 2020, Dmitry Tarakanov
 
// SPDX-License-Identifier: MIT
 
 
 
#include "TestBase.h"
 
 
 
#include <functional>
 
 
 
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 {
 
 
 
template<class T>
 
struct Storage
 
{
 
    static T value;
 
};
 
 
 
template<class T>
 
T Storage<T>::value;
 
 
 
template<class T>
 
int getDataC(lua_State* L)
 
{
 
    luabridge::Stack<T>::push(L, Storage<T>::value);
 
    return 1;
 
}
 
 
 
template<class T>
 
int setDataC(lua_State* L)
 
{
 
    Storage<T>::value = luabridge::Stack<T>::get(L, -1);
 
    return 0;
 
}
 
 
 
} // namespace
 
 
 
TEST_F(NamespaceTests, Properties_ProxyCFunctions)
 
{
 
    luabridge::getGlobalNamespace(L)
 
        .beginNamespace("ns")
 
        .addProperty("value", &getDataC<int>, &setDataC<int>)
 
        .endNamespace();
 
 
 
    Storage<int>::value = 1;
 
    runLua("ns.value = 2");
 
    ASSERT_EQ(2, Storage<int>::value);
 
 
 
    Storage<int>::value = 3;
 
    runLua("result = ns.value");
 
    ASSERT_TRUE(result().isNumber());
 
    ASSERT_EQ(3, result().cast<int>());
 
}
 
 
 
TEST_F(NamespaceTests, Properties_ProxyCFunctions_ReadOnly)
 
{
 
    luabridge::getGlobalNamespace(L)
 
        .beginNamespace("ns")
 
        .addProperty("value", &getDataC<int>)
 
        .endNamespace();
 
 
 
    Storage<int>::value = 1;
 
    ASSERT_THROW(runLua("ns.value = 2"), std::exception);
 
    ASSERT_EQ(1, Storage<int>::value);
 
 
 
    Storage<int>::value = 3;
 
    runLua("result = ns.value");
 
    ASSERT_TRUE(result().isNumber());
 
    ASSERT_EQ(3, result().cast<int>());
 
}
 
 
 
namespace {
 
struct Class
 
{
 
};
 
} // namespace
 
 
 
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: ...
 
}
 
 
 
namespace {
 
 
 
template<class T>
 
T Function(T param)
 
{
 
    return param;
 
}
 
 
 
} // namespace
 
 
 
TEST_F(NamespaceTests, Functions)
 
{
 
    luabridge::getGlobalNamespace(L).addFunction("Function", &Function<double>);
 
 
 
    runLua("result = Function (3.14)");
 
    ASSERT_TRUE(result().isNumber());
 
    ASSERT_EQ(3.14, result<double>());
 
}
 
 
 
TEST_F(NamespaceTests, StdFunctions)
 
{
 
    luabridge::getGlobalNamespace(L).addFunction("Function",
 
                                                 std::function<int(int)>(&Function<int>));
 
 
 
    runLua("result = Function (12)");
 
    ASSERT_TRUE(result().isNumber());
 
    ASSERT_EQ(12, result<int>());
 
}
 
 
 
#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