mirror of
https://github.com/hyprwm/Hyprland.git
synced 2026-08-24 10:04:19 -05:00
544 lines
17 KiB
C++
544 lines
17 KiB
C++
#include <config/lua/bindings/LuaBindingsInternal.hpp>
|
|
#include <config/lua/ConfigManager.hpp>
|
|
|
|
#include <Compositor.hpp>
|
|
|
|
#include <config/lua/types/LuaConfigInt.hpp>
|
|
#include <config/values/types/IntValue.hpp>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
#include <format>
|
|
#include <fstream>
|
|
|
|
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");
|
|
}
|
|
|
|
static void initializeOwnedLuaState(CConfigManager& mgr, const std::filesystem::path& mainConfigPath) {
|
|
mgr.m_mainConfigPath = mainConfigPath.string();
|
|
mgr.m_configPaths.clear();
|
|
mgr.m_configPaths.emplace_back(mgr.m_mainConfigPath);
|
|
mgr.reinitLuaState();
|
|
}
|
|
|
|
static lua_State* luaState(CConfigManager& mgr) {
|
|
return mgr.m_lua;
|
|
}
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
class CTempDir {
|
|
public:
|
|
CTempDir() {
|
|
const auto NOW = std::chrono::steady_clock::now().time_since_epoch().count();
|
|
m_path = std::filesystem::temp_directory_path() / std::format("hyprland-lua-require-{}", NOW);
|
|
std::filesystem::create_directories(m_path);
|
|
}
|
|
|
|
~CTempDir() {
|
|
std::error_code ec;
|
|
std::filesystem::remove_all(m_path, ec);
|
|
}
|
|
|
|
const std::filesystem::path& path() const {
|
|
return m_path;
|
|
}
|
|
|
|
private:
|
|
std::filesystem::path m_path;
|
|
};
|
|
|
|
class CScopedCompositor {
|
|
public:
|
|
CScopedCompositor() : m_prevCompositor(std::move(g_pCompositor)), m_prevKeybindManager(std::move(Keybinds::mgr())) {
|
|
g_pCompositor = makeUnique<CCompositor>(true);
|
|
Keybinds::mgr() = makeUnique<Keybinds::CKeybindManager>();
|
|
}
|
|
|
|
~CScopedCompositor() {
|
|
Keybinds::mgr() = std::move(m_prevKeybindManager);
|
|
g_pCompositor = std::move(m_prevCompositor);
|
|
}
|
|
|
|
private:
|
|
UP<CCompositor> m_prevCompositor;
|
|
UP<Keybinds::CKeybindManager> m_prevKeybindManager;
|
|
};
|
|
|
|
std::string luaString(const std::string& value) {
|
|
std::string out = "\"";
|
|
for (const auto& c : value) {
|
|
if (c == '\\' || c == '"')
|
|
out += '\\';
|
|
out += c;
|
|
}
|
|
out += '"';
|
|
return out;
|
|
}
|
|
|
|
void writeFile(const std::filesystem::path& path, const std::string& content) {
|
|
std::filesystem::create_directories(path.parent_path());
|
|
std::ofstream file(path);
|
|
file << content;
|
|
}
|
|
|
|
std::string normalizedPath(const std::filesystem::path& path) {
|
|
return path.lexically_normal().string();
|
|
}
|
|
|
|
std::string packagePath(lua_State* L) {
|
|
lua_getglobal(L, "package");
|
|
lua_getfield(L, -1, "path");
|
|
|
|
std::string path;
|
|
if (const auto* value = lua_tostring(L, -1); value)
|
|
path = value;
|
|
|
|
lua_pop(L, 2);
|
|
return path;
|
|
}
|
|
|
|
void expectTracked(CConfigManager& mgr, const std::filesystem::path& path) {
|
|
const auto& paths = mgr.getConfigPaths();
|
|
EXPECT_NE(std::ranges::find(paths, normalizedPath(path)), paths.end());
|
|
}
|
|
}
|
|
|
|
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, getMonitorsReadsOptionsFromArguments) {
|
|
CLuaState state;
|
|
const auto lua = state.get();
|
|
|
|
lua_newtable(lua);
|
|
Internal::registerQueryBindings(lua);
|
|
lua_setglobal(lua, "hl");
|
|
|
|
ASSERT_EQ(luaL_dostring(lua, R"(
|
|
assert(type(hl.get_monitors()) == "table")
|
|
assert(type(hl.get_monitors(nil)) == "table")
|
|
assert(type(hl.get_monitors({})) == "table")
|
|
assert(type(hl.get_monitors({ all = false })) == "table")
|
|
assert(type(hl.get_monitors({ all = true })) == "table")
|
|
assert(hl.get_monitors({ all = "true" }) == nil)
|
|
assert(hl.get_monitors(true) == nil)
|
|
)"),
|
|
LUA_OK)
|
|
<< lua_tostring(lua, -1);
|
|
}
|
|
|
|
TEST(ConfigLuaBindingsInternal, deprecationNoticesOnlyIncludeUsedDeprecatedValues) {
|
|
CScopedCompositor compositor;
|
|
CLuaState state;
|
|
const auto lua = state.get();
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeLuaState(mgr, lua);
|
|
|
|
lua_newtable(lua);
|
|
Internal::registerConfigRuleBindings(lua, &mgr);
|
|
lua_setglobal(lua, "hl");
|
|
|
|
const auto HANDLE = reinterpret_cast<void*>(0x1BADB002);
|
|
ASSERT_TRUE(mgr.registerPluginValue(HANDLE, makeShared<Config::Values::CIntValue>("test:ordinary", "", 0)).has_value());
|
|
ASSERT_TRUE(
|
|
mgr.registerPluginValue(HANDLE, makeShared<Config::Values::CIntValue>("test:deprecated", "", 0, Config::Values::SIntValueOptions{.deprecationNotice = "use replacement"}))
|
|
.has_value());
|
|
|
|
EXPECT_TRUE(mgr.deprecationNotices().empty());
|
|
|
|
ASSERT_EQ(luaL_dostring(lua, "hl.config({ test = { ordinary = 1 } })"), LUA_OK) << lua_tostring(lua, -1);
|
|
EXPECT_TRUE(mgr.deprecationNotices().empty());
|
|
|
|
ASSERT_EQ(luaL_dostring(lua, "hl.config({ test = { deprecated = 1 } })"), LUA_OK) << lua_tostring(lua, -1);
|
|
|
|
const auto notices = mgr.deprecationNotices();
|
|
ASSERT_EQ(notices.size(), 1);
|
|
EXPECT_EQ(notices.front(), "test.deprecated: use replacement");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, absolutePathLoadsAndTracksFile) {
|
|
CScopedCompositor compositor;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
const auto module = tmp.path() / "absolute.lua";
|
|
writeFile(mainConfig, "");
|
|
writeFile(module, "return { value = 42 }");
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
const auto L = CConfigManagerPluginLuaTestAccessor::luaState(mgr);
|
|
|
|
const auto CODE = std::format("mod = require({})", luaString(module.string()));
|
|
ASSERT_EQ(luaL_dostring(L, CODE.c_str()), LUA_OK) << lua_tostring(L, -1);
|
|
|
|
lua_getglobal(L, "mod");
|
|
ASSERT_TRUE(lua_istable(L, -1));
|
|
lua_getfield(L, -1, "value");
|
|
EXPECT_EQ(lua_tointeger(L, -1), 42);
|
|
lua_pop(L, 2);
|
|
|
|
expectTracked(mgr, module);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, relativePathResolvesFromConfigDirectory) {
|
|
CScopedCompositor compositor;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
const auto module = tmp.path() / "modules" / "relative.lua";
|
|
writeFile(mainConfig, "");
|
|
writeFile(module, "return 'relative-ok'");
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
const auto L = CConfigManagerPluginLuaTestAccessor::luaState(mgr);
|
|
|
|
ASSERT_EQ(luaL_dostring(L, R"(
|
|
mod = require("./modules/relative.lua")
|
|
)"),
|
|
LUA_OK)
|
|
<< lua_tostring(L, -1);
|
|
|
|
lua_getglobal(L, "mod");
|
|
ASSERT_TRUE(lua_isstring(L, -1));
|
|
EXPECT_STREQ(lua_tostring(L, -1), "relative-ok");
|
|
lua_pop(L, 1);
|
|
|
|
expectTracked(mgr, module);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, wildcardLoadsSortedTableAndTracksFilesAndDirectory) {
|
|
CScopedCompositor compositor;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
const auto modulesDir = tmp.path() / "modules";
|
|
const auto moduleA = modulesDir / "a.lua";
|
|
const auto moduleB = modulesDir / "b.lua";
|
|
writeFile(mainConfig, "");
|
|
writeFile(moduleB, "return 'b'");
|
|
writeFile(moduleA, "return 'a'");
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
const auto L = CConfigManagerPluginLuaTestAccessor::luaState(mgr);
|
|
|
|
ASSERT_EQ(luaL_dostring(L, R"(
|
|
mods = require("./modules/*")
|
|
assert(#mods == 2)
|
|
assert(mods[1] == "a")
|
|
assert(mods[2] == "b")
|
|
)"),
|
|
LUA_OK)
|
|
<< lua_tostring(L, -1);
|
|
|
|
expectTracked(mgr, modulesDir);
|
|
expectTracked(mgr, moduleA);
|
|
expectTracked(mgr, moduleB);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, wildcardNoMatchIsCatchableError) {
|
|
CScopedCompositor compositor;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
writeFile(mainConfig, "");
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
const auto L = CConfigManagerPluginLuaTestAccessor::luaState(mgr);
|
|
|
|
ASSERT_EQ(luaL_dostring(L, R"(
|
|
ok, err = pcall(require, "./missing/*")
|
|
assert(ok == false)
|
|
assert(type(err) == "string")
|
|
assert(string.find(err, "module './missing/*' not found", 1, true) ~= nil)
|
|
)"),
|
|
LUA_OK)
|
|
<< lua_tostring(L, -1);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, normalModuleRequireStillUsesConfigDirectoryPackagePath) {
|
|
CScopedCompositor compositor;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
const auto module = tmp.path() / "colors.lua";
|
|
writeFile(mainConfig, "");
|
|
writeFile(module, "return 'normal-ok'");
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
const auto L = CConfigManagerPluginLuaTestAccessor::luaState(mgr);
|
|
|
|
ASSERT_EQ(luaL_dostring(L, R"(
|
|
mod = require("colors")
|
|
)"),
|
|
LUA_OK)
|
|
<< lua_tostring(L, -1);
|
|
|
|
lua_getglobal(L, "mod");
|
|
ASSERT_TRUE(lua_isstring(L, -1));
|
|
EXPECT_STREQ(lua_tostring(L, -1), "normal-ok");
|
|
lua_pop(L, 1);
|
|
|
|
expectTracked(mgr, module);
|
|
}
|
|
|
|
TEST(ConfigLuaRequire, packagePathPreservesLuaDefaultsAfterConfigDirectory) {
|
|
CScopedCompositor compositor;
|
|
CLuaState defaultState;
|
|
CTempDir tmp;
|
|
const auto mainConfig = tmp.path() / "hyprland.lua";
|
|
writeFile(mainConfig, "");
|
|
|
|
const auto defaultPath = packagePath(defaultState.get());
|
|
ASSERT_FALSE(defaultPath.empty());
|
|
|
|
CConfigManager mgr;
|
|
CConfigManagerPluginLuaTestAccessor::initializeOwnedLuaState(mgr, mainConfig);
|
|
|
|
const auto configPath = std::format("{};{}", (tmp.path() / "?.lua").string(), (tmp.path() / "?/init.lua").string());
|
|
EXPECT_EQ(packagePath(CConfigManagerPluginLuaTestAccessor::luaState(mgr)), std::format("{};{}", configPath, defaultPath));
|
|
}
|