diff options
Diffstat (limited to 'tests/config/lua/LuaBindingsInternal.cpp')
| -rw-r--r-- | tests/config/lua/LuaBindingsInternal.cpp | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/tests/config/lua/LuaBindingsInternal.cpp b/tests/config/lua/LuaBindingsInternal.cpp new file mode 100644 index 00000000..d94ea2dc --- /dev/null +++ b/tests/config/lua/LuaBindingsInternal.cpp @@ -0,0 +1,256 @@ +#include <config/lua/bindings/LuaBindingsInternal.hpp> +#include <config/lua/ConfigManager.hpp> + +#include <Compositor.hpp> + +#include <config/lua/types/LuaConfigInt.hpp> + +#include <gtest/gtest.h> + +extern "C" { +#include <lualib.h> +#include <lauxlib.h> +} + +using namespace Config::Lua; +using namespace Config::Lua::Bindings; + +namespace Config::Lua { + class CConfigManagerPluginLuaTestAccessor { + public: + static void initializeLuaState(CConfigManager& mgr, lua_State* L) { + mgr.m_lua = L; + lua_pushlightuserdata(L, &mgr); + lua_setfield(L, LUA_REGISTRYINDEX, "hl_lua_manager"); + } + }; +} + +namespace { + class CLuaState { + public: + CLuaState() : m_lua(luaL_newstate()) { + luaL_openlibs(m_lua); + } + + ~CLuaState() { + if (m_lua) + lua_close(m_lua); + } + + lua_State* get() const { + return m_lua; + } + + private: + lua_State* m_lua = nullptr; + }; + + int testPluginFn(lua_State* L) { + lua_pushstring(L, "pong"); + return 1; + } +} + +TEST(ConfigLuaBindingsInternal, parseDirectionAliases) { + EXPECT_EQ(Internal::parseDirectionStr("left"), Math::DIRECTION_LEFT); + EXPECT_EQ(Internal::parseDirectionStr("l"), Math::DIRECTION_LEFT); + EXPECT_EQ(Internal::parseDirectionStr("right"), Math::DIRECTION_RIGHT); + EXPECT_EQ(Internal::parseDirectionStr("r"), Math::DIRECTION_RIGHT); + EXPECT_EQ(Internal::parseDirectionStr("up"), Math::DIRECTION_UP); + EXPECT_EQ(Internal::parseDirectionStr("t"), Math::DIRECTION_UP); + EXPECT_EQ(Internal::parseDirectionStr("down"), Math::DIRECTION_DOWN); + EXPECT_EQ(Internal::parseDirectionStr("b"), Math::DIRECTION_DOWN); + EXPECT_EQ(Internal::parseDirectionStr("???"), Math::DIRECTION_DEFAULT); +} + +TEST(ConfigLuaBindingsInternal, parseToggleAliases) { + EXPECT_EQ(Internal::parseToggleStr(""), Config::Actions::TOGGLE_ACTION_TOGGLE); + EXPECT_EQ(Internal::parseToggleStr("toggle"), Config::Actions::TOGGLE_ACTION_TOGGLE); + EXPECT_EQ(Internal::parseToggleStr("enable"), Config::Actions::TOGGLE_ACTION_ENABLE); + EXPECT_EQ(Internal::parseToggleStr("on"), Config::Actions::TOGGLE_ACTION_ENABLE); + EXPECT_EQ(Internal::parseToggleStr("disable"), Config::Actions::TOGGLE_ACTION_DISABLE); + EXPECT_EQ(Internal::parseToggleStr("off"), Config::Actions::TOGGLE_ACTION_DISABLE); +} + +TEST(ConfigLuaBindingsInternal, argStrConvertsStringsAndNumbers) { + CLuaState S; + const auto L = S.get(); + + lua_pushstring(L, "abc"); + EXPECT_EQ(Internal::argStr(L, -1), "abc"); + lua_pop(L, 1); + + lua_pushnumber(L, 42); + EXPECT_EQ(Internal::argStr(L, -1), "42"); + lua_pop(L, 1); +} + +TEST(ConfigLuaBindingsInternal, tableOptHelpersReadOptionalFields) { + CLuaState S; + const auto L = S.get(); + + lua_createtable(L, 0, 5); + lua_pushstring(L, "value"); + lua_setfield(L, -2, "s"); + lua_pushnumber(L, 5.5); + lua_setfield(L, -2, "n"); + lua_pushboolean(L, true); + lua_setfield(L, -2, "b"); + lua_pushstring(L, "not-number"); + lua_setfield(L, -2, "n2"); + lua_pushnil(L); + lua_setfield(L, -2, "nilv"); + + EXPECT_EQ(Internal::tableOptStr(L, -1, "s").value_or(""), "value"); + EXPECT_DOUBLE_EQ(Internal::tableOptNum(L, -1, "n").value_or(0), 5.5); + EXPECT_EQ(Internal::tableOptBool(L, -1, "b").value_or(false), true); + EXPECT_FALSE(Internal::tableOptNum(L, -1, "n2").has_value()); + EXPECT_FALSE(Internal::tableOptStr(L, -1, "missing").has_value()); + EXPECT_FALSE(Internal::tableOptBool(L, -1, "nilv").has_value()); + + lua_pop(L, 1); +} + +TEST(ConfigLuaBindingsInternal, selectorHelpersAcceptStringAndNumberSelectors) { + CLuaState S; + const auto L = S.get(); + + lua_createtable(L, 0, 4); + lua_pushstring(L, "DP-1"); + lua_setfield(L, -2, "monitor"); + lua_pushnumber(L, 7); + lua_setfield(L, -2, "workspace"); + lua_pushnumber(L, 1337); + lua_setfield(L, -2, "window"); + + EXPECT_EQ(Internal::tableOptMonitorSelector(L, -1, "monitor", "test.fn").value_or(""), "DP-1"); + EXPECT_EQ(Internal::tableOptWorkspaceSelector(L, -1, "workspace", "test.fn").value_or(""), "7"); + EXPECT_EQ(Internal::tableOptWindowSelector(L, -1, "window", "test.fn").value_or(""), "1337"); + + EXPECT_FALSE(Internal::tableOptMonitorSelector(L, -1, "missing", "test.fn").has_value()); + EXPECT_FALSE(Internal::tableOptWorkspaceSelector(L, -1, "missing", "test.fn").has_value()); + EXPECT_FALSE(Internal::tableOptWindowSelector(L, -1, "missing", "test.fn").has_value()); + + EXPECT_EQ(Internal::requireTableFieldMonitorSelector(L, -1, "monitor", "test.fn"), "DP-1"); + EXPECT_EQ(Internal::requireTableFieldWorkspaceSelector(L, -1, "workspace", "test.fn"), "7"); + EXPECT_EQ(Internal::requireTableFieldWindowSelector(L, -1, "window", "test.fn"), "1337"); + + lua_pop(L, 1); +} + +TEST(ConfigLuaBindingsInternal, pushWindowUpvalAcceptsNumberAndStringSelectors) { + CLuaState S; + const auto L = S.get(); + + lua_createtable(L, 0, 1); + lua_pushnumber(L, 42); + lua_setfield(L, -2, "window"); + + Internal::pushWindowUpval(L, -1); + ASSERT_TRUE(lua_isstring(L, -1)); + EXPECT_STREQ(lua_tostring(L, -1), "42"); + lua_pop(L, 1); + + lua_pushstring(L, "0xabc"); + lua_setfield(L, -2, "window"); + + Internal::pushWindowUpval(L, -1); + ASSERT_TRUE(lua_isstring(L, -1)); + EXPECT_STREQ(lua_tostring(L, -1), "0xabc"); + lua_pop(L, 1); + + lua_pushnil(L); + lua_setfield(L, -2, "window"); + + Internal::pushWindowUpval(L, -1); + EXPECT_TRUE(lua_isnil(L, -1)); + lua_pop(L, 1); + + lua_pop(L, 1); +} + +TEST(ConfigLuaBindingsInternal, parseTableFieldMissingFieldAndPrefixedErrors) { + CLuaState S; + const auto L = S.get(); + + CLuaConfigInt parser(0); + + lua_newtable(L); + auto err = Internal::parseTableField(L, -1, "required", parser); + EXPECT_EQ(err.errorCode, PARSE_ERROR_BAD_VALUE); + EXPECT_NE(err.message.find("missing required field"), std::string::npos); + lua_pop(L, 1); + + lua_createtable(L, 0, 1); + lua_pushstring(L, "bad"); + lua_setfield(L, -2, "count"); + + err = Internal::parseTableField(L, -1, "count", parser); + EXPECT_EQ(err.errorCode, PARSE_ERROR_BAD_TYPE); + EXPECT_NE(err.message.find("field \"count\":"), std::string::npos); + lua_pop(L, 1); +} + +TEST(ConfigLuaBindingsInternal, pluginBindingIsTableWithLoadFunction) { + CLuaState S; + const auto L = S.get(); + + lua_newtable(L); + Internal::registerConfigRuleBindings(L, nullptr); + + lua_getfield(L, -1, "plugin"); + ASSERT_TRUE(lua_istable(L, -1)); + + lua_getfield(L, -1, "load"); + EXPECT_TRUE(lua_isfunction(L, -1)); + lua_pop(L, 1); + + lua_pop(L, 2); +} + +TEST(ConfigLuaBindingsInternal, pluginLuaFnIsUnloadedWithoutDanglingCall) { + CLuaState S; + const auto L = S.get(); + + auto PREVCOMPOSITOR = std::move(g_pCompositor); + g_pCompositor = makeUnique<CCompositor>(true); + + CConfigManager mgr; + CConfigManagerPluginLuaTestAccessor::initializeLuaState(mgr, L); + + lua_newtable(L); + Internal::registerConfigRuleBindings(L, &mgr); + lua_setglobal(L, "hl"); + + const auto HANDLE = reinterpret_cast<void*>(0x1BADB002); + + const auto regResult = mgr.registerPluginLuaFunction(HANDLE, "demo", "ping", testPluginFn); + ASSERT_TRUE(regResult.has_value()) << regResult.error(); + + ASSERT_EQ(luaL_dostring(L, R"( + local f = hl.plugin.demo.ping + assert(type(f) == "function") + captured = f + local v = f() + assert(v == "pong") + )"), + LUA_OK); + + mgr.onPluginUnload(HANDLE); + + ASSERT_EQ(luaL_dostring(L, R"( + assert(hl.plugin.demo == nil) + )"), + LUA_OK); + + ASSERT_EQ(luaL_dostring(L, R"( + local ok, err = pcall(captured) + assert(ok == false) + assert(type(err) == "string") + assert(string.find(err, "no longer available", 1, true) ~= nil) + )"), + LUA_OK); + + g_pCompositor = std::move(PREVCOMPOSITOR); +} |
