hyprlang/tests/parse/main.cpp
2024-12-16 13:37:09 -06:00

322 lines
16 KiB
C++
Executable file

#include <iostream>
#include <filesystem>
#include <algorithm>
#include <hyprlang.hpp>
namespace Colors {
constexpr const char* RED = "\x1b[31m";
constexpr const char* GREEN = "\x1b[32m";
constexpr const char* YELLOW = "\x1b[33m";
constexpr const char* BLUE = "\x1b[34m";
constexpr const char* MAGENTA = "\x1b[35m";
constexpr const char* CYAN = "\x1b[36m";
constexpr const char* RESET = "\x1b[0m";
};
#define EXPECT(expr, val) \
if (const auto RESULT = expr; RESULT != (val)) { \
std::cout << Colors::RED << "Failed: " << Colors::RESET << #expr << ", expected " << #val << " but got " << RESULT << "\n"; \
ret = 1; \
} else { \
std::cout << Colors::GREEN << "Passed " << Colors::RESET << #expr << ". Got " << val << "\n"; \
}
// globals for testing
bool barrelRoll = false;
std::string flagsFound = "";
Hyprlang::CConfig* pConfig = nullptr;
std::string currentPath = "";
std::string ignoreKeyword = "";
std::string useKeyword = "";
static std::vector<std::string> categoryKeywordActualValues;
static Hyprlang::CParseResult handleDoABarrelRoll(const char* COMMAND, const char* VALUE) {
if (std::string(VALUE) == "woohoo, some, params")
barrelRoll = true;
Hyprlang::CParseResult result;
return result;
}
static Hyprlang::CParseResult handleFlagsTest(const char* COMMAND, const char* VALUE) {
std::string cmd = COMMAND;
flagsFound = cmd.substr(5);
Hyprlang::CParseResult result;
return result;
}
static Hyprlang::CParseResult handleCategoryKeyword(const char* COMMAND, const char* VALUE) {
categoryKeywordActualValues.push_back(VALUE);
return Hyprlang::CParseResult();
}
static Hyprlang::CParseResult handleTestIgnoreKeyword(const char* COMMAND, const char* VALUE) {
ignoreKeyword = VALUE;
return Hyprlang::CParseResult();
}
static Hyprlang::CParseResult handleTestUseKeyword(const char* COMMAND, const char* VALUE) {
useKeyword = VALUE;
return Hyprlang::CParseResult();
}
static Hyprlang::CParseResult handleNoop(const char* COMMAND, const char* VALUE) {
return Hyprlang::CParseResult();
}
static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALUE) {
std::string PATH = std::filesystem::canonical(currentPath + "/" + VALUE);
return pConfig->parseFile(PATH.c_str());
}
static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) {
if (!*data)
*data = calloc(1, sizeof(int64_t));
std::string V = VALUE;
if (V == "abc")
*reinterpret_cast<int64_t*>(*data) = 1;
else
*reinterpret_cast<int64_t*>(*data) = 2;
Hyprlang::CParseResult result;
return result;
}
static void handleCustomValueDestroy(void** data) {
if (*data)
free(*data);
}
int main(int argc, char** argv, char** envp) {
int ret = 0;
try {
if (!getenv("SHELL"))
setenv("SHELL", "/bin/sh", true);
std::cout << "Starting test\n";
Hyprlang::CConfig config("./config/config.conf", {});
pConfig = &config;
currentPath = std::filesystem::canonical("./config/");
// setup config
config.addConfigValue("testInt", (Hyprlang::INT)0);
config.addConfigValue("testFloat", 0.F);
config.addConfigValue("testVec", Hyprlang::SVector2D{69, 420});
config.addConfigValue("testString", "");
config.addConfigValue("testStringColon", "");
config.addConfigValue("testEnv", "");
config.addConfigValue("testVar", (Hyprlang::INT)0);
config.addConfigValue("categoryKeyword", (Hyprlang::STRING) "");
config.addConfigValue("testStringQuotes", "");
config.addConfigValue("testStringRecursive", "");
config.addConfigValue("testCategory:testValueInt", (Hyprlang::INT)0);
config.addConfigValue("testCategory:testValueHex", (Hyprlang::INT)0xA);
config.addConfigValue("testCategory:nested1:testValueNest", (Hyprlang::INT)0);
config.addConfigValue("testCategory:nested1:nested2:testValueNest", (Hyprlang::INT)0);
config.addConfigValue("testCategory:nested1:categoryKeyword", (Hyprlang::STRING) "");
config.addConfigValue("testDefault", (Hyprlang::INT)123);
config.addConfigValue("testCategory:testColor1", (Hyprlang::INT)0);
config.addConfigValue("testCategory:testColor2", (Hyprlang::INT)0);
config.addConfigValue("testCategory:testColor3", (Hyprlang::INT)0);
config.addConfigValue("flagsStuff:value", (Hyprlang::INT)0);
config.addConfigValue("myColors:pink", (Hyprlang::INT)0);
config.addConfigValue("myColors:green", (Hyprlang::INT)0);
config.addConfigValue("myColors:random", (Hyprlang::INT)0);
config.addConfigValue("customType", {Hyprlang::CConfigCustomValueType{&handleCustomValueSet, &handleCustomValueDestroy, "def"}});
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
config.registerHandler(&handleFlagsTest, "flags", {true});
config.registerHandler(&handleSource, "source", {false});
config.registerHandler(&handleTestIgnoreKeyword, "testIgnoreKeyword", {false});
config.registerHandler(&handleTestUseKeyword, ":testUseKeyword", {false});
config.registerHandler(&handleNoop, "testCategory:testUseKeyword", {false});
config.registerHandler(&handleCategoryKeyword, "testCategory:categoryKeyword", {false});
config.addSpecialCategory("special", {"key"});
config.addSpecialConfigValue("special", "value", (Hyprlang::INT)0);
config.addSpecialCategory("specialAnonymous", {nullptr, false, true});
config.addSpecialConfigValue("specialAnonymous", "value", (Hyprlang::INT)0);
config.addConfigValue("multiline", "");
config.commence();
config.addSpecialCategory("specialGeneric:one", {nullptr, true});
config.addSpecialConfigValue("specialGeneric:one", "value", (Hyprlang::INT)0);
config.addSpecialCategory("specialGeneric:two", {nullptr, true});
config.addSpecialConfigValue("specialGeneric:two", "value", (Hyprlang::INT)0);
const Hyprlang::CConfigValue copyTest = {(Hyprlang::INT)1};
config.addSpecialConfigValue("specialGeneric:one", "copyTest", copyTest);
const auto PARSERESULT = config.parse();
if (PARSERESULT.error) {
std::cout << "Parse error: " << PARSERESULT.getError() << "\n";
return 1;
}
EXPECT(PARSERESULT.error, false);
// test values
std::cout << " → Testing values\n";
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testInt")), 123);
EXPECT(std::any_cast<float>(config.getConfigValue("testFloat")), 123.456f);
auto EXP = Hyprlang::SVector2D{69, 420};
EXPECT(std::any_cast<Hyprlang::SVector2D>(config.getConfigValue("testVec")), EXP);
EXPECT(std::any_cast<const char*>(config.getConfigValue("testString")), std::string{"Hello World! # This is not a comment!"});
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringQuotes")), std::string{"\"Hello World!\""});
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testValueInt")), (Hyprlang::INT)123456);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testValueHex")), (Hyprlang::INT)0xFFFFAABB);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:nested1:testValueNest")), (Hyprlang::INT)1);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:nested1:nested2:testValueNest")), (Hyprlang::INT)1);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testDefault")), (Hyprlang::INT)123);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor1")), (Hyprlang::INT)0xFFFFFFFF);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor2")), (Hyprlang::INT)0xFF000000);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testColor3")), (Hyprlang::INT)0x22ffeeff);
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"ee:ee:ee"});
EXPECT(std::any_cast<const char*>(config.getConfigValue("categoryKeyword")), std::string{"oops, this one shouldn't call the handler, not fun"});
EXPECT(std::any_cast<const char*>(config.getConfigValue("testCategory:nested1:categoryKeyword")), std::string{"this one should not either"});
EXPECT(ignoreKeyword, "aaa");
EXPECT(useKeyword, "yes");
// test static values
std::cout << " → Testing static values\n";
static auto* const PTESTINT = config.getConfigValuePtr("testInt")->getDataStaticPtr();
EXPECT(*reinterpret_cast<int64_t*>(*PTESTINT), 123);
config.parse();
EXPECT(*reinterpret_cast<int64_t*>(*PTESTINT), 123);
// test handlers
std::cout << " → Testing handlers\n";
EXPECT(barrelRoll, true);
EXPECT(flagsFound, std::string{"abc"});
EXPECT(categoryKeywordActualValues.at(0), "we are having fun");
EXPECT(categoryKeywordActualValues.at(1), "so much fun");
EXPECT(categoryKeywordActualValues.at(2), "im the fun one at parties");
// test dynamic
std::cout << " → Testing dynamic\n";
barrelRoll = false;
EXPECT(config.parseDynamic("doABarrelRoll = woohoo, some, params").error, false);
EXPECT(barrelRoll, true);
EXPECT(config.parseDynamic("testCategory:testValueHex", "0xaabbccdd").error, false);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testCategory:testValueHex")), (Hyprlang::INT)0xAABBCCDD);
EXPECT(config.parseDynamic("testStringColon", "1:3:3:7").error, false);
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringColon")), std::string{"1:3:3:7"});
EXPECT(config.parseDynamic("flagsStuff:value = 69").error, false);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("flagsStuff:value")), (Hyprlang::INT)69);
// test dynamic special
config.addSpecialConfigValue("specialGeneric:one", "boom", (Hyprlang::INT)0);
EXPECT(config.parseDynamic("specialGeneric:one:boom = 1").error, false);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "boom")), (Hyprlang::INT)1);
// test variables
std::cout << " → Testing variables\n";
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 13371337);
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"abc"});
// test dynamic variables
std::cout << " → Testing dynamic variables\n";
EXPECT(config.parseDynamic("$MY_VAR_2 = 420").error, false);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("testVar")), 1337420);
EXPECT(config.parseDynamic("$RECURSIVE1 = d").error, false);
EXPECT(std::any_cast<const char*>(config.getConfigValue("testStringRecursive")), std::string{"dbc"});
// test env variables
std::cout << " → Testing env variables\n";
EXPECT(std::any_cast<const char*>(config.getConfigValue("testEnv")), std::string{getenv("SHELL")});
// test special categories
std::cout << " → Testing special categories\n";
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "a")), 1);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 2);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "value")), 1);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:two", "value")), 2);
EXPECT(config.parseDynamic("special[b]:value = 3").error, false);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "b")), 3);
// test dynamic special variable
EXPECT(config.parseDynamic("$SPECIALVAL1 = 2").error, false);
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("special", "value", "a")), (Hyprlang::INT)2);
// test copying
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialGeneric:one", "copyTest")), 2);
// test listing keys
EXPECT(config.listKeysForSpecialCategory("special")[1], "b");
// test anonymous
EXPECT(config.listKeysForSpecialCategory("specialAnonymous").size(), 2);
const auto KEYS = config.listKeysForSpecialCategory("specialAnonymous");
EXPECT(std::any_cast<int64_t>(config.getSpecialConfigValue("specialAnonymous", "value", KEYS[1].c_str())), 3);
// test sourcing
std::cout << " → Testing sourcing\n";
EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:pink")), (Hyprlang::INT)0xFFc800c8);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:green")), (Hyprlang::INT)0xFF14f014);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:random")), (Hyprlang::INT)0xFFFF1337);
// test custom type
std::cout << " → Testing custom types\n";
EXPECT(*reinterpret_cast<int64_t*>(std::any_cast<void*>(config.getConfigValue("customType"))), (Hyprlang::INT)1);
// test multiline config
EXPECT(std::any_cast<const char*>(config.getConfigValue("multiline")), std::string{"very long command"});
std::cout << " → Testing error.conf\n";
Hyprlang::CConfig errorConfig("./config/error.conf", {.verifyOnly = true, .throwAllErrors = true});
errorConfig.commence();
const auto ERRORS = errorConfig.parse();
EXPECT(ERRORS.error, true);
const auto ERRORSTR = std::string{ERRORS.getError()};
EXPECT(std::count(ERRORSTR.begin(), ERRORSTR.end(), '\n'), 1);
std::cout << " → Testing invalid-numbers.conf\n";
Hyprlang::CConfig invalidNumbersConfig("./config/invalid-numbers.conf", {.throwAllErrors = true});
invalidNumbersConfig.addConfigValue("invalidHex", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("emptyHex", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("hugeHex", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("invalidInt", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("emptyInt", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("invalidColor", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("invalidFirstCharColor", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("invalidColorAlpha", (Hyprlang::INT)0);
invalidNumbersConfig.addConfigValue("invalidFirstCharColorAlpha", (Hyprlang::INT)0);
invalidNumbersConfig.commence();
const auto ERRORS2 = invalidNumbersConfig.parse();
EXPECT(ERRORS2.error, true);
const auto ERRORSTR2 = std::string{ERRORS2.getError()};
EXPECT(std::count(ERRORSTR2.begin(), ERRORSTR2.end(), '\n'), 9 - 1);
Hyprlang::CConfig multilineErrorConfig("./config/multiline-errors.conf", {.verifyOnly = true, .throwAllErrors = true});
multilineErrorConfig.commence();
const auto ERRORS3 = multilineErrorConfig.parse();
EXPECT(ERRORS3.error, true);
const auto ERRORSTR3 = std::string{ERRORS3.getError()};
// Error on line 12
EXPECT(ERRORSTR3.contains("12"), true);
// Backslash at end of file
EXPECT(ERRORSTR3.contains("backslash"), true);
} catch (const char* e) {
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
return 1;
}
return ret;
}