diff options
32 files changed, 1131 insertions, 1648 deletions
diff --git a/hyprtester/src/Log.hpp b/hyprtester/src/Log.hpp index 7eeb3ece..87f0890d 100644 --- a/hyprtester/src/Log.hpp +++ b/hyprtester/src/Log.hpp @@ -3,6 +3,8 @@ #include <format> #include <print> +#include "shared.hpp" + namespace NLog { template <typename... Args> //NOLINTNEXTLINE @@ -11,7 +13,7 @@ namespace NLog { logMsg += std::vformat(fmt.get(), std::make_format_args(args...)); - std::println("{}", logMsg); + std::println("{}{}", logMsg, Colors::RESET); std::fflush(stdout); } -}
\ No newline at end of file +} diff --git a/hyprtester/src/hyprctlCompat.cpp b/hyprtester/src/hyprctlCompat.cpp index 2c8e0644..dbeee93d 100644 --- a/hyprtester/src/hyprctlCompat.cpp +++ b/hyprtester/src/hyprctlCompat.cpp @@ -43,7 +43,7 @@ std::vector<SInstanceData> instances() { } catch (std::exception& e) { return {}; } for (const auto& el : std::filesystem::directory_iterator(getRuntimeDir())) { - if (!el.is_directory() || !std::filesystem::exists(el.path().string() + "/hyprland.lock")) + if (!std::filesystem::exists(el.path() / "hyprland.lock")) continue; // read lock @@ -74,7 +74,7 @@ std::vector<SInstanceData> instances() { std::erase_if(result, [&](const auto& el) { return kill(el.pid, 0) != 0 && errno == ESRCH; }); - std::sort(result.begin(), result.end(), [&](const auto& a, const auto& b) { return a.time < b.time; }); + std::ranges::sort(result, [&](const auto& a, const auto& b) { return a.time < b.time; }); return result; } @@ -135,4 +135,4 @@ std::string getFromSocket(const std::string& cmd) { close(SERVERSOCKET); return reply; -}
\ No newline at end of file +} diff --git a/hyprtester/src/main.cpp b/hyprtester/src/main.cpp index 2fc78201..0da9a20e 100644 --- a/hyprtester/src/main.cpp +++ b/hyprtester/src/main.cpp @@ -17,14 +17,17 @@ #include "shared.hpp" #include "hyprctlCompat.hpp" #include "tests/main/tests.hpp" +#undef TEST_CASES_STORAGE // Prevent redefinition warning #include "tests/clients/tests.hpp" +#undef TEST_CASES_STORAGE // Prevent redefinition warning #include "tests/plugin/plugin.hpp" +#include "tests/shared.hpp" +#include <algorithm> #include <filesystem> #include <hyprutils/os/Process.hpp> #include <hyprutils/memory/WeakPtr.hpp> #include <hyprutils/memory/Casts.hpp> -using namespace Hyprutils::Memory; #include <csignal> #include <cerrno> @@ -38,35 +41,13 @@ using namespace Hyprutils::Memory; using namespace Hyprutils::OS; using namespace Hyprutils::Memory; +using Path = std::filesystem::path; #define SP CSharedPointer -static int ret = 0; -static SP<CProcess> hyprlandProc; -static const std::string cwd = std::filesystem::current_path().string(); - -// -static bool launchHyprland(std::string configPath, std::string binaryPath) { - if (binaryPath == "") { - std::error_code ec; - if (!std::filesystem::exists(cwd + "/../build/Hyprland", ec) || ec) { - NLog::log("{}No Hyprland binary", Colors::RED); - return false; - } - - binaryPath = cwd + "/../build/Hyprland"; - } - - if (configPath == "") { - std::error_code ec; - if (!std::filesystem::exists(cwd + "/test.lua", ec) || ec) { - NLog::log("{}No test config", Colors::RED); - return false; - } - - configPath = cwd + "/test.lua"; - } +static SP<CProcess> hyprlandProc; +static bool launchHyprland(Path configPath, Path binaryPath) { NLog::log("{}Launching Hyprland", Colors::YELLOW); hyprlandProc = makeShared<CProcess>(binaryPath, std::vector<std::string>{"--config", configPath}); hyprlandProc->addEnv("HYPRLAND_HEADLESS_ONLY", "1"); @@ -78,121 +59,147 @@ static bool launchHyprland(std::string configPath, std::string binaryPath) { static bool hyprlandAlive() { NLog::log("{}hyprlandAlive", Colors::YELLOW); - kill(hyprlandProc->pid(), 0); - return errno != ESRCH; + return kill(hyprlandProc->pid(), 0) == 0 || errno != ESRCH; } -static void help() { +[[noreturn]] static void helpAndDie(int exit_code) { NLog::log("usage: hyprtester [arg [...]].\n"); NLog::log(R"(Arguments: --help -h - Show this message again - --config FILE -c FILE - Specify config file to use - --binary FILE -b FILE - Specify Hyprland binary to use - --plugin FILE -p FILE - Specify the location of the test plugin)"); -} - -int main(int argc, char** argv, char** envp) { - - std::string configPath = ""; - std::string binaryPath = ""; - std::string pluginPath = std::filesystem::current_path().string(); - - if (argc > 1) { - std::span<char*> args{argv + 1, sc<std::size_t>(argc - 1)}; + --config FILE -c FILE - Specify config file to use (default: './test.lua') + --binary FILE -b FILE - Specify Hyprland binary to use (default: '../build/Hyprland') + --plugin FILE -p FILE - Specify the location of the test plugin (default: './'))"); - for (auto it = args.begin(); it != args.end(); it++) { - std::string_view value = *it; - - if (value == "--config" || value == "-c") { - if (std::next(it) == args.end()) { - help(); - - return 1; - } - - configPath = *std::next(it); - - try { - configPath = std::filesystem::canonical(configPath); - - if (!std::filesystem::is_regular_file(configPath)) { - throw std::exception(); - } - } catch (...) { - std::println(stderr, "[ ERROR ] Config file '{}' doesn't exist!", configPath); - help(); - - return 1; - } - - it++; + std::exit(exit_code); +} - continue; - } else if (value == "--binary" || value == "-b") { - if (std::next(it) == args.end()) { - help(); +static Path validatePathOrDie(Path path) { + try { + if (!std::filesystem::is_regular_file(path)) { + throw std::exception(); + } + } catch (...) { + std::println(stderr, "[ ERROR ] File '{}' is not accessible or not a regular file", path.string()); + helpAndDie(EXIT_FAILURE); + } + return path; +} - return 1; - } +namespace { + struct SSettings { + Path configPath; + Path binaryPath; + Path pluginPath; + }; +} - binaryPath = *std::next(it); +static SSettings parseSettings(const std::span<const char*> args) { + static const auto cwd = std::filesystem::current_path(); + SSettings settings{}; - try { - binaryPath = std::filesystem::canonical(binaryPath); + for (auto it = args.begin(); it < args.end(); it++) { + std::string_view value = *it; - if (!std::filesystem::is_regular_file(binaryPath)) { - throw std::exception(); - } - } catch (...) { - std::println(stderr, "[ ERROR ] Binary '{}' doesn't exist!", binaryPath); - help(); + if (value == "--config" || value == "-c") { + if (std::next(it) == args.end()) { + helpAndDie(EXIT_FAILURE); + } - return 1; - } + settings.configPath = validatePathOrDie(*std::next(it)); + it++; + } else if (value == "--binary" || value == "-b") { + if (std::next(it) == args.end()) { + helpAndDie(EXIT_FAILURE); + } - it++; + settings.binaryPath = validatePathOrDie(*std::next(it)); + it++; + } else if (value == "--plugin" || value == "-p") { + if (std::next(it) == args.end()) { + helpAndDie(EXIT_FAILURE); + } - continue; - } else if (value == "--plugin" || value == "-p") { - if (std::next(it) == args.end()) { - help(); + settings.pluginPath = validatePathOrDie(*std::next(it)); + it++; + } else if (value == "--help" || value == "-h") { + helpAndDie(EXIT_SUCCESS); + } else { + std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it); + helpAndDie(EXIT_SUCCESS); + } + } - return 1; - } + // Default options + if (settings.configPath.empty()) + settings.configPath = validatePathOrDie(cwd / "test.lua"); + if (settings.binaryPath.empty()) + settings.binaryPath = validatePathOrDie(cwd / "../build/Hyprland"); + if (settings.pluginPath.empty()) + settings.pluginPath = cwd; - pluginPath = *std::next(it); + return settings; +} - try { - pluginPath = std::filesystem::canonical(pluginPath); +static bool preTestCleanup() { + bool failed = false; - if (!std::filesystem::is_regular_file(pluginPath)) { - throw std::exception(); - } - } catch (...) { - std::println(stderr, "[ ERROR ] plugin '{}' doesn't exist!", pluginPath); - help(); + if (!Tests::killAllWindows()) { + NLog::log("{}Internal failure: failed to kill all windows", Colors::RED); + failed = true; + } + if (!Tests::killAllLayers()) { + NLog::log("{}Internal failure: failed to kill all layers", Colors::RED); + failed = true; + } + if (getFromSocket("/reload") != "ok") { + NLog::log("{}Internal failure: failed to reload", Colors::RED); + failed = true; + } + if (!getFromSocket("/activeworkspace").contains("workspace ID 1 (1)")) { + if (getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })") != "ok") { + NLog::log("{}Internal failure: failed to switch to workspace 1", Colors::RED); + failed = true; + } + } + if (getFromSocket("/dispatch hl.dsp.cursor.move({ x = 960, y = 540 })") != "ok") { + NLog::log("{}Internal failure: failed to reset cursor position", Colors::RED); + failed = true; + } - return 1; - } + return !failed; +} - it++; +static void runTests(std::map<const char*, CTestCase&>& testCases) { + for (auto& [name, tc] : testCases) { + // Clean up before every test + NLog::log("{}Cleaning up", Colors::YELLOW); + (void)preTestCleanup(); + + NLog::log("{}Running test {}", Colors::BLUE, name); + tc.test(); + if (tc.failed) + NLog::log("{}Test failed: {}", Colors::RED, name); + else + NLog::log("{}Test passed: {}", Colors::GREEN, name); + } +} - continue; - } else if (value == "--help" || value == "-h") { - help(); +static long long countFailed(const std::map<const char*, CTestCase&>& testCases) { + long long ans = 0; + for (const auto& [_, tc] : testCases) { + if (tc.failed) + ans++; + } + return ans; +} - return 0; - } else { - std::println(stderr, "[ ERROR ] Unknown option '{}' !", *it); - help(); +int main(int argc, char** argv, char** envp) { - return 1; - } - } - } + std::span<const char*> args{const_cast<const char**>(argv + 1), sc<std::size_t>(argc - 1)}; + const SSettings settings = parseSettings(args); NLog::log("{}launching hl", Colors::YELLOW); - if (!launchHyprland(configPath, binaryPath)) { + if (!launchHyprland(settings.configPath, settings.binaryPath)) { NLog::log("{}well it failed", Colors::RED); return 1; } @@ -220,7 +227,7 @@ int main(int argc, char** argv, char** envp) { getFromSocket("/output create headless"); NLog::log("{}trying to load plugin", Colors::YELLOW); - if (const auto R = getFromSocket(std::format("/plugin load {}", pluginPath)); R != "ok") { + if (const auto R = getFromSocket(std::format("/plugin load {}", settings.pluginPath.string())); R != "ok") { NLog::log("{}Failed to load the test plugin: {}", Colors::RED, R); getFromSocket("/dispatch hl.dsp.exit()"); return 1; @@ -228,34 +235,50 @@ int main(int argc, char** argv, char** envp) { NLog::log("{}Loaded plugin", Colors::YELLOW); - NLog::log("{}Running main tests", Colors::YELLOW); + long long failedTests = 0, totalTests = 0; - for (const auto& fn : testFns) { - EXPECT(fn(), true); - } + NLog::log("{}Running main tests", Colors::YELLOW); + runTests(mainTestCases); + failedTests += countFailed(mainTestCases); + totalTests += mainTestCases.size(); NLog::log("{}Running protocol client tests", Colors::YELLOW); + runTests(clientTestCases); + failedTests += countFailed(clientTestCases); + totalTests += clientTestCases.size(); - for (const auto& fn : clientTestFns) { - EXPECT(fn(), true); - } + // TODO: the two tests below should not be hardcoded, include them somewhere NLog::log("{}running plugin test", Colors::YELLOW); - EXPECT(testPlugin(), true); + if (!testPlugin()) { + NLog::log("{}Test failed: plugin test", Colors::RED); + failedTests++; + } else { + NLog::log("{}Test passed: plugin test", Colors::GREEN); + } + totalTests++; NLog::log("{}running vkb test from plugin", Colors::YELLOW); - EXPECT(testVkb(), true); + if (!testVkb()) { + NLog::log("{}Test failed: vkb test from plugin", Colors::RED); + failedTests++; + } else { + NLog::log("{}Test passed: vkb test from plugin", Colors::GREEN); + } + totalTests++; // kill hyprland NLog::log("{}dispatching exit", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.exit()"); - NLog::log("\n{}Summary:\n\tPASSED: {}{}{}/{}\n\tFAILED: {}{}{}/{}\n{}", Colors::RESET, Colors::GREEN, TESTS_PASSED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, Colors::RED, - TESTS_FAILED, Colors::RESET, TESTS_PASSED + TESTS_FAILED, (TESTS_FAILED > 0 ? std::string{Colors::RED} + "\nSome tests failed.\n" : "")); + NLog::log("\nSummary:\n\tPASSED: {}{}{}/{}", Colors::GREEN, totalTests - failedTests, Colors::RESET, totalTests); + NLog::log("\tFAILED: {}{}{}/{}", Colors::RED, failedTests, Colors::RESET, totalTests); + if (failedTests > 0) + NLog::log("{}Some tests failed.", Colors::RED); kill(hyprlandProc->pid(), SIGKILL); hyprlandProc.reset(); - return ret || TESTS_FAILED; + return failedTests > 0; } diff --git a/hyprtester/src/shared.hpp b/hyprtester/src/shared.hpp index 941788fd..d40b2f1f 100644 --- a/hyprtester/src/shared.hpp +++ b/hyprtester/src/shared.hpp @@ -1,12 +1,12 @@ // Stolen from hyprutils #pragma once -#include <iostream> +#include <cmath> +#include <string> -inline std::string HIS = ""; -inline std::string WLDISPLAY = ""; -inline int TESTS_PASSED = 0; -inline int TESTS_FAILED = 0; +// TODO: localize these global variables +inline std::string HIS = ""; +inline std::string WLDISPLAY = ""; namespace Colors { constexpr const char* RED = "\x1b[31m"; @@ -18,94 +18,229 @@ namespace Colors { constexpr const char* RESET = "\x1b[0m"; }; +// ================================= +// TEST CASES DEFINITION +// ================================= + +class CTestCase { + public: + CTestCase() = default; + CTestCase(const CTestCase&) = delete; // Test cases probably should not be copied + bool failed = false; + virtual ~CTestCase() = default; + + // TODO: `test` will be protected + virtual void test() = 0; +}; + +#define TEST_CASE(name) \ + namespace { \ + class TestCase_##name : public CTestCase { \ + public: \ + void test() override; \ + }; \ + } \ + \ + static TestCase_##name test_case_##name{}; \ + static auto register_test_case_##name = [] { \ + /* `TEST_CASES_STORAGE` must be defined by the caller */ \ + TEST_CASES_STORAGE.emplace(#name, test_case_##name); \ + return 1; \ + }(); \ + \ + void TestCase_##name::test() + +#define SUBTEST(name, ...) \ + namespace { \ + class Subtest_##name { \ + public: \ + bool failed = false; \ + \ + void main(__VA_ARGS__); \ + }; \ + } \ + \ + void Subtest_##name::main(__VA_ARGS__) + +#define CALL_SUBTEST(name, ...) \ + do { \ + auto subtest_##name = Subtest_##name{}; \ + subtest_##name.main(__VA_ARGS__); \ + if (subtest_##name.failed) { \ + NLog::log("{}Subtest {}({}) failed", Colors::RED, #name, #__VA_ARGS__); \ + this->failed = true; \ + return; \ + } \ + } while (0) + +// ================================= +// IN-TEST MACROS +// ================================= + +/// Marks the test as failed without terminating it +#define MARK_TEST_FAILED_SILENT() this->failed = true + +/// Prints a failure message and makrs the test as failed without terminating it +#define MARK_TEST_FAILED(fmt, ...) \ + do { \ + NLog::log("{}Failed:{} " fmt ". Source: {}@{}.", Colors::RED, Colors::RESET __VA_OPT__(, ) __VA_ARGS__, __FILE__, __LINE__); \ + MARK_TEST_FAILED_SILENT(); \ + } while (0) + +/// Terminates the test execution and marks it as failed +#define FAIL_TEST_SILENT() \ + do { \ + MARK_TEST_FAILED_SILENT(); \ + return; \ + } while (0) + +/// Prints a failure message, terminates the test execution, and marks it as failed +#define FAIL_TEST(fmt, ...) \ + do { \ + MARK_TEST_FAILED(fmt, __VA_ARGS__); \ + return; \ + } while (0) + +#define LOG_OK(fmt, ...) \ + do { \ + NLog::log("{}OK:{} " fmt ". Source: {}@{}", Colors::GREEN, Colors::RESET, __VA_ARGS__, __FILE__, __LINE__); \ + } while (0) + +// In case of failure: +// - All the `EXPECT*` macros will print a message and mark the test as failed without terminating its execution; +// - All the `ASSERT*` macros will print a message, mark the test as failed, and terminate it. +// +// `OK` acts like `ASSERT_OK`. + #define EXPECT_MAX_DELTA(expr, desired, delta) \ if (const auto RESULT = expr; std::abs(RESULT - (desired)) > delta) { \ - NLog::log("{}Failed: {}{}, expected max delta of {}, got delta {} ({} - {}). Source: {}@{}.", Colors::RED, Colors::RESET, #expr, delta, (RESULT - (desired)), RESULT, \ - desired, __FILE__, __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED("{}, expected max delta of {}, got delta {} ({} - {})", #expr, delta, (RESULT - (desired)), RESULT, desired); \ } else { \ - NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, (RESULT - (desired))); \ - TESTS_PASSED++; \ + LOG_OK("{}. Got {}", #expr, (RESULT - (desired))); \ } #define EXPECT(expr, val) \ - if (const auto RESULT = expr; RESULT != (val)) { \ - NLog::log("{}Failed: {}{}, expected {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ - } else { \ - NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \ - TESTS_PASSED++; \ - } + do { \ + if (const auto RESULT = expr; RESULT != (val)) { \ + MARK_TEST_FAILED("{}, expected {}, got {}", #expr, val, RESULT); \ + } else { \ + LOG_OK("{}. Got {}", #expr, val); \ + } \ + } while (0) #define EXPECT_NOT(expr, val) \ if (const auto RESULT = expr; RESULT == (val)) { \ - NLog::log("{}Failed: {}{}, expected not {}, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, val, RESULT, __FILE__, __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED("{}, expected not {}, got {}", #expr, val, RESULT); \ } else { \ - NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, val); \ - TESTS_PASSED++; \ + LOG_OK("{}. Got {}", #expr, val); \ } #define EXPECT_VECTOR2D(expr, val) \ do { \ const auto& RESULT = expr; \ - const auto& EXPECTED = val; \ - if (!(std::abs(RESULT.x - EXPECTED.x) < 1e-6 && std::abs(RESULT.y - EXPECTED.y) < 1e-6)) { \ - NLog::log("{}Failed: {}{}, expected [{}, {}], got [{}, {}]. Source: {}@{}.", Colors::RED, Colors::RESET, #expr, EXPECTED.x, EXPECTED.y, RESULT.x, RESULT.y, __FILE__, \ - __LINE__); \ - ret = 1; \ - TESTS_FAILED++; \ + const auto& ASSERTED = val; \ + if (!(std::abs(RESULT.x - ASSERTED.x) < 1e-6 && std::abs(RESULT.y - ASSERTED.y) < 1e-6)) { \ + MARK_TEST_FAILED("{}, expected [{}, {}], got [{}, {}]", #expr, ASSERTED.x, ASSERTED.y, RESULT.x, RESULT.y); \ } else { \ - NLog::log("{}Passed: {}{}. Got [{}, {}].", Colors::GREEN, Colors::RESET, #expr, RESULT.x, RESULT.y); \ - TESTS_PASSED++; \ + LOG_OK("{}. Got [{}, {}].", #expr, RESULT.x, RESULT.y); \ } \ } while (0) +// String check macros below use a special error message layout, putting the Source reference before `haystack`, +// since `haystack` may be a large multi-line string. Thus, they use bare `NLog::log` + `MARK_TEST_FAILED_SILENT`. + #define EXPECT_CONTAINS(haystack, needle) \ - if (const auto EXPECTED = needle; !std::string{haystack}.contains(EXPECTED)) { \ + if (const auto ASSERTED = needle; !std::string{haystack}.contains(ASSERTED)) { \ NLog::log("{}Failed: {}{} should contain {} but doesn't. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, #needle, __FILE__, __LINE__, \ std::string{haystack}); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED_SILENT(); \ } else { \ - NLog::log("{}Passed: {}{} contains {}.", Colors::GREEN, Colors::RESET, #haystack, EXPECTED); \ - TESTS_PASSED++; \ + LOG_OK("{} contains {}.", #haystack, ASSERTED); \ } #define EXPECT_NOT_CONTAINS(haystack, needle) \ if (std::string{haystack}.contains(needle)) { \ NLog::log("{}Failed: {}{} shouldn't contain {} but does. Source: {}@{}. Haystack is:\n{}", Colors::RED, Colors::RESET, #haystack, #needle, __FILE__, __LINE__, \ std::string{haystack}); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED_SILENT(); \ } else { \ - NLog::log("{}Passed: {}{} doesn't contain {}.", Colors::GREEN, Colors::RESET, #haystack, #needle); \ - TESTS_PASSED++; \ + LOG_OK("{} doesn't contain {}.", #haystack, #needle); \ } #define EXPECT_STARTS_WITH(str, what) \ if (!std::string{str}.starts_with(what)) { \ NLog::log("{}Failed: {}{} should start with {} but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, __FILE__, __LINE__, \ std::string{str}); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED_SILENT(); \ } else { \ - NLog::log("{}Passed: {}{} starts with {}.", Colors::GREEN, Colors::RESET, #str, #what); \ - TESTS_PASSED++; \ + LOG_OK("{} starts with {}.", #str, #what); \ } #define EXPECT_COUNT_STRING(str, what, no) \ if (Tests::countOccurrences(str, what) != no) { \ NLog::log("{}Failed: {}{} should contain {} {} times, but doesn't. Source: {}@{}. String is:\n{}", Colors::RED, Colors::RESET, #str, #what, no, __FILE__, __LINE__, \ std::string{str}); \ - ret = 1; \ - TESTS_FAILED++; \ + MARK_TEST_FAILED_SILENT(); \ } else { \ - NLog::log("{}Passed: {}{} contains {} {} times.", Colors::GREEN, Colors::RESET, #str, #what, no); \ - TESTS_PASSED++; \ + LOG_OK("{} contains {} {} times.", #str, #what, no); \ } -#define OK(x) EXPECT(x, "ok") +#define EXPECT_OK(x) EXPECT(x, "ok") + +#define ASSERT_MAX_DELTA(expr, desired, delta) \ + do { \ + EXPECT_MAX_DELTA(expr, desired, delta); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT(expr, val) \ + do { \ + EXPECT(expr, val); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_NOT(expr, val) \ + do { \ + EXPECT_NOT(expr, val); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_VECTOR2D(expr, val) \ + do { \ + EXPECT_VECTOR2D(expr, val); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_CONTAINS(haystack, needle) \ + do { \ + EXPECT_CONTAINS(haystack, needle); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_NOT_CONTAINS(haystack, needle) \ + do { \ + EXPECT_NOT_CONTAINS(haystack, needle); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_STARTS_WITH(str, what) \ + do { \ + EXPECT_STARTS_WITH(str, what); \ + if (this->failed) \ + return; \ + } while (0) + +#define ASSERT_COUNT_STRING(str, what, no) \ + do { \ + EXPECT_COUNT_STRING(str, what, no); \ + if (this->failed) \ + return; \ + } while (0) + +#define OK(x) ASSERT(x, "ok") diff --git a/hyprtester/src/tests/clients/child-window.cpp b/hyprtester/src/tests/clients/child-window.cpp index 40118c7c..f97ffde1 100644 --- a/hyprtester/src/tests/clients/child-window.cpp +++ b/hyprtester/src/tests/clients/child-window.cpp @@ -1,4 +1,3 @@ -#include "../../shared.hpp" #include "../../hyprctlCompat.hpp" #include "../shared.hpp" #include "tests.hpp" @@ -7,6 +6,7 @@ #include <hyprutils/os/FileDescriptor.hpp> #include <hyprutils/os/Process.hpp> +#include <optional> #include <sys/poll.h> #include <unistd.h> #include <csignal> @@ -17,15 +17,6 @@ using namespace Hyprutils::Memory; #define SP CSharedPointer -struct SClient { - SP<CProcess> proc; - std::array<char, 1024> readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; - static bool waitForWindow(SP<CProcess> proc, int windowsBefore) { int counter = 0; while (Tests::processAlive(proc->pid()) && Tests::windowCount() == windowsBefore) { @@ -40,56 +31,69 @@ static bool waitForWindow(SP<CProcess> proc, int windowsBefore) { return Tests::processAlive(proc->pid()); } -static bool startClient(SClient& client) { +namespace { + class CClient { + SP<CProcess> proc; + std::array<char, 1024> readBuf; + CFileDescriptor readFd, writeFd; + struct pollfd fds; + + public: + CClient(); + ~CClient(); + bool createChild(); + }; +} + +CClient::CClient() { NLog::log("{}Attempting to start child-window client", Colors::YELLOW); - client.proc = makeShared<CProcess>(binaryDir + "/child-window", std::vector<std::string>{}); + this->proc = makeShared<CProcess>(binaryDir + "/child-window", std::vector<std::string>{}); - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); + this->proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); int procInPipeFd[2], procOutPipeFd[2]; if (pipe(procInPipeFd) != 0 || pipe(procOutPipeFd) != 0) { NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; + throw std::exception(); } - client.writeFd = CFileDescriptor(procInPipeFd[1]); - client.proc->setStdinFD(procInPipeFd[0]); + this->writeFd = CFileDescriptor(procInPipeFd[1]); + this->proc->setStdinFD(procInPipeFd[0]); - client.readFd = CFileDescriptor(procOutPipeFd[0]); - client.proc->setStdoutFD(procOutPipeFd[1]); + this->readFd = CFileDescriptor(procOutPipeFd[0]); + this->proc->setStdoutFD(procOutPipeFd[1]); - if (!client.proc->runAsync()) { + if (!this->proc->runAsync()) { NLog::log("{}Failed to run client", Colors::RED); - return false; + throw std::exception(); } close(procInPipeFd[0]); close(procOutPipeFd[1]); - if (!waitForWindow(client.proc, Tests::windowCount())) { + if (!waitForWindow(this->proc, Tests::windowCount())) { NLog::log("{}Window took too long to open", Colors::RED); - return false; + throw std::exception(); } NLog::log("{}Started child-window client", Colors::YELLOW); - return true; } -static void stopClient(SClient& client) { +CClient::~CClient() { std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); + write(this->writeFd.get(), cmd.c_str(), cmd.length()); - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); + kill(this->proc->pid(), SIGKILL); + this->proc.reset(); } -static bool createChild(SClient& client) { +bool CClient::createChild() { std::string cmd = "toplevel\n"; - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) + if ((size_t)write(this->writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) return false; - if (!waitForWindow(client.proc, Tests::windowCount())) + if (!waitForWindow(this->proc, Tests::windowCount())) NLog::log("{}Child window took too long to open", Colors::RED); if (getFromSocket("/dispatch hl.dsp.focus({ window = 'class:child-test-child' })") != "ok") { @@ -100,19 +104,21 @@ static bool createChild(SClient& client) { return true; } -static bool test() { - SClient client; +TEST_CASE(childWindow) { + { + std::optional<CClient> client; + try { + client.emplace(); + } catch (...) { FAIL_TEST("Couldn't start the client"); } - if (!startClient(client)) - return false; - OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set', window = 'class:child-test-parent' })")); - OK(getFromSocket("/dispatch hl.dsp.window.pin({ action = 'set', window = 'class:child-test-parent' })")); + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set', window = 'class:child-test-parent' })")); + OK(getFromSocket("/dispatch hl.dsp.window.pin({ action = 'set', window = 'class:child-test-parent' })")); - createChild(client); - EXPECT(Tests::windowCount(), 2) - EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2); + client->createChild(); + EXPECT(Tests::windowCount(), 2); + EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2); + } - stopClient(client); NLog::log("{}Reloading config", Colors::YELLOW); OK(getFromSocket("/reload")); Tests::killAllWindows(); @@ -122,30 +128,27 @@ static bool test() { NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN); auto kitty = Tests::spawnKitty(); if (!kitty) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Couldn't spawn kitty"); } // create group and enable auto-grouping OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })")); - SClient client2; - if (!startClient(client2)) - return false; + { + std::optional<CClient> client2; + try { + client2.emplace(); + } catch (...) { FAIL_TEST("Couldn't start the client"); } - EXPECT(Tests::windowCount(), 2); - createChild(client2); - EXPECT(Tests::windowCount(), 3); + EXPECT(Tests::windowCount(), 2); + client2->createChild(); + EXPECT(Tests::windowCount(), 3); - // child has set_parent so shouldBeFloated returns true, it should not be auto-grouped - EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1); + // child has set_parent so shouldBeFloated returns true, it should not be auto-grouped + EXPECT_COUNT_STRING(getFromSocket("/clients"), "grouped: 0", 1); + } - stopClient(client2); Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); - - return !ret; } - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/pointer-scroll.cpp b/hyprtester/src/tests/clients/pointer-scroll.cpp index 86ce55f0..c468deb3 100644 --- a/hyprtester/src/tests/clients/pointer-scroll.cpp +++ b/hyprtester/src/tests/clients/pointer-scroll.cpp @@ -1,4 +1,3 @@ -#include "../../shared.hpp" #include "../../hyprctlCompat.hpp" #include "../shared.hpp" #include "tests.hpp" @@ -7,6 +6,7 @@ #include <hyprutils/os/FileDescriptor.hpp> #include <hyprutils/os/Process.hpp> +#include <optional> #include <sys/poll.h> #include <unistd.h> #include <csignal> @@ -17,100 +17,103 @@ using namespace Hyprutils::Memory; #define SP CSharedPointer -struct SClient { - SP<CProcess> proc; - std::array<char, 1024> readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; +namespace { + class CClient { + SP<CProcess> proc; + std::array<char, 1024> readBuf; + CFileDescriptor readFd, writeFd; + struct pollfd fds; + + public: + CClient(); + ~CClient(); + int getLastDelta(); + }; +} -static bool startClient(SClient& client) { - client.proc = makeShared<CProcess>(binaryDir + "/pointer-scroll", std::vector<std::string>{}); +CClient::CClient() { + this->proc = makeShared<CProcess>(binaryDir + "/pointer-scroll", std::vector<std::string>{}); - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); + this->proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); int pipeFds1[2], pipeFds2[2]; if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; + throw std::exception(); } - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); + this->writeFd = CFileDescriptor(pipeFds1[1]); + this->proc->setStdinFD(pipeFds1[0]); - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); + this->readFd = CFileDescriptor(pipeFds2[0]); + this->proc->setStdoutFD(pipeFds2[1]); const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); + this->proc->runAsync(); close(pipeFds1[0]); close(pipeFds2[1]); - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) - return false; + this->fds = {.fd = this->readFd.get(), .events = POLLIN}; + if (poll(&this->fds, 1, 1000) != 1 || !(this->fds.revents & POLLIN)) + throw std::exception(); - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; + this->readBuf.fill(0); + if (read(this->readFd.get(), this->readBuf.data(), this->readBuf.size() - 1) == -1) + throw std::exception(); - std::string ret = std::string{client.readBuf.data()}; + std::string ret = std::string{this->readBuf.data()}; if (ret.find("started") == std::string::npos) { NLog::log("{}Failed to start pointer-scroll client, read {}", Colors::RED, ret); - return false; + throw std::exception(); } // wait for window to appear int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { + while (Tests::processAlive(this->proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { counter++; std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (counter > 50) { NLog::log("{}pointer-scroll client took too long to open", Colors::RED); - return false; + throw std::exception(); } } - if (getFromSocket(std::format("/dispatch hl.dsp.window.set_prop({{ window = 'pid:{}', prop = 'no_anim', value = '1' }})", client.proc->pid())) != "ok") { + if (getFromSocket(std::format("/dispatch hl.dsp.window.set_prop({{ window = 'pid:{}', prop = 'no_anim', value = '1' }})", this->proc->pid())) != "ok") { NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); - return false; + throw std::exception(); } - if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", client.proc->pid())) != "ok") { + if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", this->proc->pid())) != "ok") { NLog::log("{}Failed to focus pointer-scroll client", Colors::RED, ret); - return false; + throw std::exception(); } NLog::log("{}Started pointer-scroll client", Colors::YELLOW); - - return true; } -static void stopClient(SClient& client) { +CClient::~CClient() { std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); + write(this->writeFd.get(), cmd.c_str(), cmd.length()); - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); + kill(this->proc->pid(), SIGKILL); + this->proc.reset(); } -static int getLastDelta(SClient& client) { +int CClient::getLastDelta() { std::string cmd = "hypr"; - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) + if ((size_t)write(this->writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) return false; - if (poll(&client.fds, 1, 1500) != 1 || !(client.fds.revents & POLLIN)) + if (poll(&this->fds, 1, 1500) != 1 || !(this->fds.revents & POLLIN)) return false; - ssize_t bytesRead = read(client.fds.fd, client.readBuf.data(), 1023); + ssize_t bytesRead = read(this->fds.fd, this->readBuf.data(), 1023); if (bytesRead == -1) return false; - client.readBuf[bytesRead] = 0; - std::string received = std::string{client.readBuf.data()}; + this->readBuf[bytesRead] = 0; + std::string received = std::string{this->readBuf.data()}; received.pop_back(); try { @@ -122,35 +125,29 @@ static bool sendScroll(int delta) { return getFromSocket(std::format("/eval hl.plugin.test.scroll({})", delta)) == "ok"; } -static bool test() { - SClient client; - - if (!startClient(client)) - return false; +TEST_CASE(pointerScroll) { + std::optional<CClient> client; + try { + client.emplace(); + } catch (...) { FAIL_TEST("Couldn't start the client"); } EXPECT(getFromSocket("r/eval hl.config({ input = { emulate_discrete_scroll = 0 } })"), "ok"); EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 10); + EXPECT(client->getLastDelta(), 10); EXPECT(getFromSocket("r/eval hl.config({ input = { scroll_factor = 2 } })"), "ok"); EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 20); + EXPECT(client->getLastDelta(), 20); EXPECT(getFromSocket("r/eval hl.device({ name = 'test-mouse-1', scroll_factor = 3 })"), "ok"); EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 30); + EXPECT(client->getLastDelta(), 30); EXPECT(getFromSocket("r/dispatch hl.dsp.window.set_prop({ window = 'active', prop = 'scroll_mouse', value = '4' })"), "ok"); EXPECT(sendScroll(10), true); - EXPECT(getLastDelta(client), 40); - - stopClient(client); + EXPECT(client->getLastDelta(), 40); NLog::log("{}Reloading the config", Colors::YELLOW); OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/pointer-warp.cpp b/hyprtester/src/tests/clients/pointer-warp.cpp index fa6334d3..e13e03f6 100644 --- a/hyprtester/src/tests/clients/pointer-warp.cpp +++ b/hyprtester/src/tests/clients/pointer-warp.cpp @@ -1,4 +1,3 @@ -#include "../../shared.hpp" #include "../../hyprctlCompat.hpp" #include "../shared.hpp" #include "tests.hpp" @@ -7,6 +6,7 @@ #include <hyprutils/os/FileDescriptor.hpp> #include <hyprutils/os/Process.hpp> +#include <optional> #include <sys/poll.h> #include <unistd.h> #include <csignal> @@ -17,102 +17,105 @@ using namespace Hyprutils::Memory; #define SP CSharedPointer -struct SClient { - SP<CProcess> proc; - std::array<char, 1024> readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; - -static int ret = 0; +namespace { + class CClient { + SP<CProcess> proc; + std::array<char, 1024> readBuf; + CFileDescriptor readFd, writeFd; + struct pollfd fds; + + public: + CClient(); + ~CClient(); + bool sendWarp(int x, int y); + }; +} -static bool startClient(SClient& client) { - client.proc = makeShared<CProcess>(binaryDir + "/pointer-warp", std::vector<std::string>{}); +CClient::CClient() { + this->proc = makeShared<CProcess>(binaryDir + "/pointer-warp", std::vector<std::string>{}); - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); + this->proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); int pipeFds1[2], pipeFds2[2]; if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; + throw std::exception(); } - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); + this->writeFd = CFileDescriptor(pipeFds1[1]); + this->proc->setStdinFD(pipeFds1[0]); - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); + this->readFd = CFileDescriptor(pipeFds2[0]); + this->proc->setStdoutFD(pipeFds2[1]); const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); + this->proc->runAsync(); close(pipeFds1[0]); close(pipeFds2[1]); - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) - return false; + this->fds = {.fd = this->readFd.get(), .events = POLLIN}; + if (poll(&this->fds, 1, 1000) != 1 || !(this->fds.revents & POLLIN)) + throw std::exception(); - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; + this->readBuf.fill(0); + if (read(this->readFd.get(), this->readBuf.data(), this->readBuf.size() - 1) == -1) + throw std::exception(); - std::string ret = std::string{client.readBuf.data()}; + std::string ret = std::string{this->readBuf.data()}; if (ret.find("started") == std::string::npos) { NLog::log("{}Failed to start pointer-warp client, read {}", Colors::RED, ret); - return false; + throw std::exception(); } // wait for window to appear int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { + while (Tests::processAlive(this->proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { counter++; std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (counter > 100) { NLog::log("{}pointer-warp client took too long to open, continuing", Colors::YELLOW); - break; + throw std::exception(); } } - if (getFromSocket(std::format("/dispatch hl.dsp.window.set_prop({{ window = 'pid:{}', prop = 'no_anim', value = '1' }})", client.proc->pid())) != "ok") { + if (getFromSocket(std::format("/dispatch hl.dsp.window.set_prop({{ window = 'pid:{}', prop = 'no_anim', value = '1' }})", this->proc->pid())) != "ok") { NLog::log("{}Failed to disable animations for client window", Colors::RED, ret); - return false; + throw std::exception(); } - if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", client.proc->pid())) != "ok") { + if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", this->proc->pid())) != "ok") { NLog::log("{}Failed to focus pointer-warp client", Colors::RED, ret); - return false; + throw std::exception(); } NLog::log("{}Started pointer-warp client", Colors::YELLOW); - - return true; } -static void stopClient(SClient& client) { +CClient::~CClient() { std::string cmd = "exit\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); + write(this->writeFd.get(), cmd.c_str(), cmd.length()); - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); + kill(this->proc->pid(), SIGKILL); + this->proc.reset(); } // format is like below // "warp 20 20\n" would ask to warp cursor to x=20,y=20 in surface local coords -static bool sendWarp(SClient& client, int x, int y) { +bool CClient::sendWarp(int x, int y) { std::string cmd = std::format("warp {} {}\n", x, y); - if ((size_t)write(client.writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) + if ((size_t)write(this->writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length()) return false; - if (poll(&client.fds, 1, 1500) != 1 || !(client.fds.revents & POLLIN)) + if (poll(&this->fds, 1, 1500) != 1 || !(this->fds.revents & POLLIN)) return false; - ssize_t bytesRead = read(client.fds.fd, client.readBuf.data(), 1023); + ssize_t bytesRead = read(this->fds.fd, this->readBuf.data(), 1023); if (bytesRead == -1) return false; - client.readBuf[bytesRead] = 0; - std::string recieved = std::string{client.readBuf.data()}; + this->readBuf[bytesRead] = 0; + std::string recieved = std::string{this->readBuf.data()}; recieved.pop_back(); return true; @@ -147,48 +150,40 @@ static bool isCursorPos(int x, int y) { return clientX == x && clientY == y; } -static bool test() { - SClient client; +TEST_CASE(pointerWarp) { + std::optional<CClient> client; - if (!startClient(client)) - return false; + try { + client.emplace(); + } catch (...) { FAIL_TEST("Couldn't start the client"); } - EXPECT(sendWarp(client, 100, 100), true); + EXPECT(client->sendWarp(100, 100), true); EXPECT(isCursorPos(100, 100), true); - EXPECT(sendWarp(client, 0, 0), true); + EXPECT(client->sendWarp(0, 0), true); EXPECT(isCursorPos(0, 0), true); - EXPECT(sendWarp(client, 200, 200), true); + EXPECT(client->sendWarp(200, 200), true); EXPECT(isCursorPos(200, 200), true); - EXPECT(sendWarp(client, 100, -100), true); + EXPECT(client->sendWarp(100, -100), true); EXPECT(isCursorPos(200, 200), true); - EXPECT(sendWarp(client, 234, 345), true); + EXPECT(client->sendWarp(234, 345), true); EXPECT(isCursorPos(234, 345), true); - EXPECT(sendWarp(client, -1, -1), true); + EXPECT(client->sendWarp(-1, -1), true); EXPECT(isCursorPos(234, 345), true); - EXPECT(sendWarp(client, 1, -1), true); + EXPECT(client->sendWarp(1, -1), true); EXPECT(isCursorPos(234, 345), true); - EXPECT(sendWarp(client, 13, 37), true); + EXPECT(client->sendWarp(13, 37), true); EXPECT(isCursorPos(13, 37), true); - EXPECT(sendWarp(client, -100, 100), true); + EXPECT(client->sendWarp(-100, 100), true); EXPECT(isCursorPos(13, 37), true); - EXPECT(sendWarp(client, -1, 1), true); + EXPECT(client->sendWarp(-1, 1), true); EXPECT(isCursorPos(13, 37), true); - - stopClient(client); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/shortcut-inhibitor.cpp b/hyprtester/src/tests/clients/shortcut-inhibitor.cpp index 1039916a..491b6f47 100644 --- a/hyprtester/src/tests/clients/shortcut-inhibitor.cpp +++ b/hyprtester/src/tests/clients/shortcut-inhibitor.cpp @@ -1,4 +1,3 @@ -#include "../../shared.hpp" #include "../../hyprctlCompat.hpp" #include "../shared.hpp" #include "tests.hpp" @@ -7,6 +6,7 @@ #include <hyprutils/os/FileDescriptor.hpp> #include <hyprutils/os/Process.hpp> +#include <optional> #include <sys/poll.h> #include <csignal> #include <thread> @@ -17,106 +17,109 @@ using namespace Hyprutils::Memory; #define SP CSharedPointer -struct SClient { - SP<CProcess> proc; - std::array<char, 1024> readBuf; - CFileDescriptor readFd, writeFd; - struct pollfd fds; -}; +namespace { -static int ret = 0; + class CClient { + SP<CProcess> proc; + std::array<char, 1024> readBuf; + CFileDescriptor readFd, writeFd; + struct pollfd fds; -static bool startClient(SClient& client) { + public: + CClient(); + ~CClient(); + }; +} + +CClient::CClient() { Tests::killAllWindows(); - client.proc = makeShared<CProcess>(binaryDir + "/shortcut-inhibitor", std::vector<std::string>{}); + this->proc = makeShared<CProcess>(binaryDir + "/shortcut-inhibitor", std::vector<std::string>{}); - client.proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); + this->proc->addEnv("WAYLAND_DISPLAY", WLDISPLAY); int pipeFds1[2], pipeFds2[2]; if (pipe(pipeFds1) != 0 || pipe(pipeFds2) != 0) { NLog::log("{}Unable to open pipe to client", Colors::RED); - return false; + throw std::exception(); } - client.writeFd = CFileDescriptor(pipeFds1[1]); - client.proc->setStdinFD(pipeFds1[0]); + this->writeFd = CFileDescriptor(pipeFds1[1]); + this->proc->setStdinFD(pipeFds1[0]); - client.readFd = CFileDescriptor(pipeFds2[0]); - client.proc->setStdoutFD(pipeFds2[1]); + this->readFd = CFileDescriptor(pipeFds2[0]); + this->proc->setStdoutFD(pipeFds2[1]); const int COUNT_BEFORE = Tests::windowCount(); - client.proc->runAsync(); + this->proc->runAsync(); close(pipeFds1[0]); close(pipeFds2[1]); - client.fds = {.fd = client.readFd.get(), .events = POLLIN}; - if (poll(&client.fds, 1, 1000) != 1 || !(client.fds.revents & POLLIN)) { + this->fds = {.fd = this->readFd.get(), .events = POLLIN}; + if (poll(&this->fds, 1, 1000) != 1 || !(this->fds.revents & POLLIN)) { NLog::log("{}shortcut-inhibitor client failed poll", Colors::RED); - return false; + throw std::exception(); } - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) { + this->readBuf.fill(0); + if (read(this->readFd.get(), this->readBuf.data(), this->readBuf.size() - 1) == -1) { NLog::log("{}shortcut-inhibitor client read failed", Colors::RED); - return false; + throw std::exception(); } - std::string ret = std::string{client.readBuf.data()}; + std::string ret = std::string{this->readBuf.data()}; if (ret.find("started") == std::string::npos) { NLog::log("{}Failed to start shortcut-inhibitor client, read {}", Colors::RED, ret); - return false; + throw std::exception(); } // wait for window to appear int counter = 0; - while (Tests::processAlive(client.proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { + while (Tests::processAlive(this->proc->pid()) && Tests::windowCount() == COUNT_BEFORE) { counter++; std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (counter > 50) { NLog::log("{}shortcut-inhibitor client took too long to open", Colors::RED); - return false; + throw std::exception(); } } - if (!Tests::processAlive(client.proc->pid())) { + if (!Tests::processAlive(this->proc->pid())) { NLog::log("{}shortcut-inhibitor client not alive", Colors::RED); - return false; + throw std::exception(); } - if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", client.proc->pid())) != "ok") { + if (getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", this->proc->pid())) != "ok") { NLog::log("{}Failed to focus shortcut-inhibitor client", Colors::RED, ret); - return false; + throw std::exception(); } std::string command = "on\n"; - if (write(client.writeFd.get(), command.c_str(), command.length()) == -1) { + if (write(this->writeFd.get(), command.c_str(), command.length()) == -1) { NLog::log("{}shortcut-inhibitor client write failed", Colors::RED); - return false; + throw std::exception(); } - client.readBuf.fill(0); - if (read(client.readFd.get(), client.readBuf.data(), client.readBuf.size() - 1) == -1) - return false; + this->readBuf.fill(0); + if (read(this->readFd.get(), this->readBuf.data(), this->readBuf.size() - 1) == -1) + throw std::exception(); - ret = std::string{client.readBuf.data()}; + ret = std::string{this->readBuf.data()}; if (ret.find("inhibiting") == std::string::npos) { NLog::log("{}shortcut-inhibitor client didn't return inhibiting", Colors::RED); - return false; + throw std::exception(); } NLog::log("{}Started shortcut-inhibitor client", Colors::YELLOW); - - return true; } -static void stopClient(SClient& client) { +CClient::~CClient() { std::string cmd = "off\n"; - write(client.writeFd.get(), cmd.c_str(), cmd.length()); + write(this->writeFd.get(), cmd.c_str(), cmd.length()); - kill(client.proc->pid(), SIGKILL); - client.proc.reset(); + kill(this->proc->pid(), SIGKILL); + this->proc.reset(); } static std::string flagFile = "/tmp/hyprtester-keybinds.txt"; @@ -138,10 +141,12 @@ static bool attemptCheckFlag(int attempts, int intervalMs) { return false; } -static bool test() { - SClient client; - if (!startClient(client)) - return false; +TEST_CASE(shortcutInhibitor) { + std::optional<CClient> client; + + try { + client.emplace(); + } catch (...) { FAIL_TEST("Couldn't start the client"); } NLog::log("{}Testing keybinds", Colors::GREEN); //basic keybind test @@ -168,13 +173,4 @@ static bool test() { //gesture bypass flag test OK(getFromSocket("/eval hl.plugin.test.gesture('right', 2)")); EXPECT_CONTAINS(getFromSocket("/activewindow"), "floating: 1"); - - stopClient(client); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_CLIENT_TEST_FN(test); diff --git a/hyprtester/src/tests/clients/tests.hpp b/hyprtester/src/tests/clients/tests.hpp index 31746fe5..7f10fba7 100644 --- a/hyprtester/src/tests/clients/tests.hpp +++ b/hyprtester/src/tests/clients/tests.hpp @@ -1,12 +1,9 @@ #pragma once +#include <map> -#include <vector> -#include <functional> +#include "../../shared.hpp" -inline std::vector<std::function<bool()>> clientTestFns; +inline std::map<const char*, CTestCase&> clientTestCases; -#define REGISTER_CLIENT_TEST_FN(fn) \ - static auto _register_fn = [] { \ - clientTestFns.emplace_back(fn); \ - return 1; \ - }(); +// Where `TEST_CASE` macros will store generated test cases: +#define TEST_CASES_STORAGE clientTestCases diff --git a/hyprtester/src/tests/main/animations.cpp b/hyprtester/src/tests/main/animations.cpp index e464dcbd..2f918d2b 100644 --- a/hyprtester/src/tests/main/animations.cpp +++ b/hyprtester/src/tests/main/animations.cpp @@ -5,18 +5,11 @@ #include <hyprutils/os/Process.hpp> #include <hyprutils/memory/WeakPtr.hpp> -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; -static bool test() { - NLog::log("{}Testing animations", Colors::GREEN); - +TEST_CASE(animationsTrivial) { auto str = getFromSocket("/animations"); NLog::log("{}Testing bezier curve output from `hyprctl animations`", Colors::YELLOW); - {EXPECT_CONTAINS(str, std::format("beziers:\n\n\tname: quick\n\t\tX0: 0.15\n\t\tY0: 0.00\n\t\tX1: 0.10\n\t\tY1: 1.00"))}; - return !ret; + ASSERT_CONTAINS(str, std::format("beziers:\n\n\tname: quick\n\t\tX0: 0.15\n\t\tY0: 0.00\n\t\tX1: 0.10\n\t\tY1: 1.00")); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/colors.cpp b/hyprtester/src/tests/main/colors.cpp index ee80f24f..93dcbb65 100644 --- a/hyprtester/src/tests/main/colors.cpp +++ b/hyprtester/src/tests/main/colors.cpp @@ -6,32 +6,24 @@ #include <chrono> #include <thread> -static int ret = 0; - -static bool test() { - NLog::log("{}Testing hyprctl monitors", Colors::GREEN); - +TEST_CASE(monitorsColorManagement) { std::string monitorsSpec = getFromSocket("j/monitors"); - EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset")"); + ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset")"); - EXPECT_CONTAINS(getFromSocket("/eval hl.monitor({ output = 'HEADLESS-2', bitdepth = 10, cm = 'wide' })"), "ok") + ASSERT_CONTAINS(getFromSocket("/eval hl.monitor({ output = 'HEADLESS-2', bitdepth = 10, cm = 'wide' })"), "ok"); // monitor settings are applied after a frame is pushed. std::this_thread::sleep_for(std::chrono::milliseconds(500)); monitorsSpec = getFromSocket("j/monitors"); - EXPECT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "wide")"); + ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "wide")"); - EXPECT_CONTAINS(getFromSocket("/eval hl.monitor({ output = 'HEADLESS-2', bitdepth = 10, cm = 'srgb', sdrbrightness = 1.2, sdrsaturation = 0.98 })"), "ok") + ASSERT_CONTAINS(getFromSocket("/eval hl.monitor({ output = 'HEADLESS-2', bitdepth = 10, cm = 'srgb', sdrbrightness = 1.2, sdrsaturation = 0.98 })"), "ok"); monitorsSpec = getFromSocket("j/monitors"); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - EXPECT_CONTAINS(monitorsSpec, "colorManagementPreset"); - EXPECT_CONTAINS(monitorsSpec, "sdrBrightness"); - EXPECT_CONTAINS(monitorsSpec, "sdrSaturation"); - - return !ret; + ASSERT_CONTAINS(monitorsSpec, "colorManagementPreset"); + ASSERT_CONTAINS(monitorsSpec, "sdrBrightness"); + ASSERT_CONTAINS(monitorsSpec, "sdrSaturation"); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp index b0bbe9a9..5641f8ac 100644 --- a/hyprtester/src/tests/main/dwindle.cpp +++ b/hyprtester/src/tests/main/dwindle.cpp @@ -3,15 +3,10 @@ #include "../../hyprctlCompat.hpp" #include "tests.hpp" -static int ret = 0; - -static void testFloatClamp() { +TEST_CASE(dwindleFloatClamp) { for (auto const& win : {"a", "b", "c"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -30,25 +25,16 @@ static void testFloatClamp() { } OK(getFromSocket("/eval hl.config({ dwindle = { force_split = 0 } })")); - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - OK(getFromSocket("/reload")); } -static void test13349() { +TEST_CASE(dwindleIssue13349) { // Test if dwindle properly uses a focal point to place a new window. // exposed by #13349 as a regression from #12890 for (auto const& win : {"a", "b", "c"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -75,13 +61,9 @@ static void test13349() { EXPECT_CONTAINS(str, "at: 967,547"); EXPECT_CONTAINS(str, "size: 931,511"); } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static void testSplit() { +TEST_CASE(dwindleSplit) { // Test various split methods Tests::spawnKitty("a"); @@ -130,22 +112,16 @@ static void testSplit() { EXPECT_CONTAINS(str, "at: 22,859"); EXPECT_CONTAINS(str, "size: 1876,199"); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static void testRotatesplit() { +TEST_CASE(dwindleRotateSplit) { OK(getFromSocket("r/eval hl.config({ general = { gaps_in = 0 } })")); OK(getFromSocket("r/eval hl.config({ general = { gaps_out = 0 } })")); OK(getFromSocket("r/eval hl.config({ general = { border_size = 0 } })")); for (auto const& win : {"a", "b"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -220,19 +196,14 @@ static void testRotatesplit() { EXPECT_CONTAINS(str, "at: 960,0"); EXPECT_CONTAINS(str, "size: 960,1080"); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - OK(getFromSocket("/reload")); } -static void testForceSplitOnMoveToWorkspace() { +TEST_CASE(dwindleForceSplitOnMoveToWorkspace) { OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '2' })")); - EXPECT(!!Tests::spawnKitty("kitty"), true); + ASSERT(!!Tests::spawnKitty("kitty"), true); OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); - EXPECT(!!Tests::spawnKitty("kitty"), true); + ASSERT(!!Tests::spawnKitty("kitty"), true); std::string posBefore = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); OK(getFromSocket("/eval hl.config({ dwindle = { force_split = 2 } })")); @@ -242,38 +213,4 @@ static void testForceSplitOnMoveToWorkspace() { // Should be moved to the right, so the position should change std::string activeWindow = getFromSocket("/activewindow"); EXPECT(activeWindow.contains(posBefore), false); - - // clean up - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - Tests::waitUntilWindowsN(0); -} - -static bool test() { - NLog::log("{}Testing Dwindle layout", Colors::GREEN); - - // test - NLog::log("{}Testing float clamp", Colors::GREEN); - testFloatClamp(); - - NLog::log("{}Testing #13349", Colors::GREEN); - test13349(); - - NLog::log("{}Testing splits", Colors::GREEN); - testSplit(); - - NLog::log("{}Testing rotatesplit", Colors::GREEN); - testRotatesplit(); - - NLog::log("{}Testing force_split on move to workspace", Colors::GREEN); - testForceSplitOnMoveToWorkspace(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/exec.cpp b/hyprtester/src/tests/main/exec.cpp index 099f2cbc..ff19edfe 100644 --- a/hyprtester/src/tests/main/exec.cpp +++ b/hyprtester/src/tests/main/exec.cpp @@ -8,8 +8,6 @@ #include <hyprutils/memory/WeakPtr.hpp> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; @@ -18,9 +16,7 @@ using namespace Hyprutils::Memory; const static auto SLEEP_DURATIONS = std::array{1, 10}; -static bool test() { - NLog::log("{}Testing process spawning", Colors::GREEN); - +TEST_CASE(processSpawning) { for (const auto duration : SLEEP_DURATIONS) { // Note: POSIX sleep does not support fractional seconds, so // can't sleep for less than 1 second. @@ -45,17 +41,9 @@ static bool test() { // Ensure that sleep did not become a zombie EXPECT(Tests::processAlive(sleepPid), false); - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; + // Test succeeded + return; } - return false; + FAIL_TEST_SILENT(); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/follow_mouse_shrink.cpp b/hyprtester/src/tests/main/follow_mouse_shrink.cpp index b41e9fd6..0e12915b 100644 --- a/hyprtester/src/tests/main/follow_mouse_shrink.cpp +++ b/hyprtester/src/tests/main/follow_mouse_shrink.cpp @@ -7,17 +7,6 @@ #include "../shared.hpp" #include "tests.hpp" -static int ret = 0; - -static bool spawnKitty(const std::string& class_) { - NLog::log("{}Spawning {}", Colors::YELLOW, class_); - if (!Tests::spawnKitty(class_)) { - NLog::log("{}Error: {} did not spawn", Colors::RED, class_); - return false; - } - return true; -} - static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) { std::string activeWin = getFromSocket("/activewindow"); auto winClass = Tests::getWindowAttribute(activeWin, "class:"); @@ -43,7 +32,8 @@ static bool waitForActiveWindow(const std::string& class_, char fullscreen = '0' return true; } -static bool test() { +// TODO: split this into multiple test cases +TEST_CASE(followMouseShrink) { NLog::log("{}Testing follow_mouse_shrink", Colors::GREEN); getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:follow_mouse_shrink' })"); @@ -56,15 +46,15 @@ static bool test() { // Spawn two floating windows with a 20px gap // fms_a: position (100,100), size 400x400 -> hitbox [100,499] x [100,499] - if (!spawnKitty("fms_a")) - return false; + if (!Tests::spawnKitty("fms_a")) + FAIL_TEST("Couldn't spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 400, y = 400 })")); OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 100, y = 100 })")); // fms_b: position (520,100), size 400x400 -> hitbox [520,919] x [100,499] - if (!spawnKitty("fms_b")) - return false; + if (!Tests::spawnKitty("fms_b")) + FAIL_TEST("Couldn't spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 400, y = 400 })")); OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 520, y = 100 })")); @@ -74,58 +64,50 @@ static bool test() { OK(getFromSocket("/eval hl.config({ input = { follow_mouse_shrink = 0 } })")); // Focus A explicitly, then move cursor inside A so follow_mouse tracks it OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:fms_a' })")); - EXPECT(waitForActiveWindow("fms_a"), true); + ASSERT(waitForActiveWindow("fms_a"), true); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 300, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Move to just inside B's left edge OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 521, y = 300 })")); - EXPECT(waitForActiveWindow("fms_b"), true); + ASSERT(waitForActiveWindow("fms_b"), true); // --- Test 2: Shrink=20, cursor in dead zone does NOT change focus --- NLog::log("{}Test 2: shrink=20, cursor in B's dead zone stays on A", Colors::GREEN); OK(getFromSocket("/eval hl.config({ input = { follow_mouse_shrink = 20 } })")); // Focus A explicitly OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:fms_a' })")); - EXPECT(waitForActiveWindow("fms_a"), true); + ASSERT(waitForActiveWindow("fms_a"), true); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 300, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(200)); // Move to 530,300 -- 10px inside B, within 20px shrink zone (B's shrunk hitbox starts at 540) OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 530, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - EXPECT(isActiveWindow("fms_a"), true); + ASSERT(isActiveWindow("fms_a"), true); // --- Test 3: Shrink=20, cursor well inside inactive window DOES focus it --- NLog::log("{}Test 3: shrink=20, cursor at B's center focuses B", Colors::GREEN); // Still focused on A from test 2 OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 720, y = 300 })")); - EXPECT(waitForActiveWindow("fms_b"), true); + ASSERT(waitForActiveWindow("fms_b"), true); // --- Test 4: Focused window's hitbox is NOT shrunk --- NLog::log("{}Test 4a: focused window hitbox is not shrunk", Colors::GREEN); // Focus A explicitly, move cursor inside A, then move near A's right edge OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:fms_a' })")); - EXPECT(waitForActiveWindow("fms_a"), true); + ASSERT(waitForActiveWindow("fms_a"), true); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 300, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(200)); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 490, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - EXPECT(isActiveWindow("fms_a"), true); + ASSERT(isActiveWindow("fms_a"), true); NLog::log("{}Test 4b: inactive window hitbox IS shrunk at same position", Colors::GREEN); // Focus B explicitly, then move to (490,300). Now A is inactive with shrunk box ending at 479, so 490 is outside A. OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:fms_b' })")); - EXPECT(waitForActiveWindow("fms_b"), true); + ASSERT(waitForActiveWindow("fms_b"), true); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 720, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(200)); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 490, y = 300 })")); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - EXPECT(isActiveWindow("fms_b"), true); - - // Cleanup - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - return ret == 0; + ASSERT(isActiveWindow("fms_b"), true); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/gestures.cpp b/hyprtester/src/tests/main/gestures.cpp index 131061c4..f8324712 100644 --- a/hyprtester/src/tests/main/gestures.cpp +++ b/hyprtester/src/tests/main/gestures.cpp @@ -10,14 +10,13 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer +// TODO: refactor and reuse `Tests::waitUntilWindowsN` static bool waitForWindowCount(int expectedWindowCnt, std::string_view expectation, int waitMillis = 100, int maxWaitCnt = 50) { int counter = 0; while (Tests::windowCount() != expectedWindowCnt) { @@ -32,17 +31,10 @@ static bool waitForWindowCount(int expectedWindowCnt, std::string_view expectati return true; } -static bool test() { - NLog::log("{}Testing gestures", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - // test on workspace "window" - NLog::log("{}Switching to workspace 1", Colors::YELLOW); - getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); // no OK: we might be on 1 already - +// TODO: decompose this into multiple test cases +TEST_CASE(gestures) { Tests::spawnKitty(); - EXPECT(Tests::windowCount(), 1); + ASSERT(Tests::windowCount(), 1); // Give the shell a moment to initialize std::this_thread::sleep_for(std::chrono::milliseconds(500)); @@ -148,7 +140,7 @@ static bool test() { EXPECT(waitForWindowCount(0, "Gesture closed kitty"), true); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // This test ensures that `movecursortocorner`, which expects // a single-character direction argument, is parsed correctly. @@ -186,18 +178,4 @@ static bool test() { OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - // reload cfg - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/groups.cpp b/hyprtester/src/tests/main/groups.cpp index 71b5cf18..7e1c63b6 100644 --- a/hyprtester/src/tests/main/groups.cpp +++ b/hyprtester/src/tests/main/groups.cpp @@ -10,17 +10,14 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer -static bool test() { - NLog::log("{}Testing groups", Colors::GREEN); - +// TODO: decompose this into multiple test cases +TEST_CASE(groups) { // test on workspace "window" NLog::log("{}Dispatching workspace `groups`", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:groups' })"); @@ -28,8 +25,7 @@ static bool test() { NLog::log("{}Testing movewindoworgroup from group to group", Colors::YELLOW); auto kittyA = Tests::spawnKitty("kittyA"); if (!kittyA) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } // check kitty properties. One kitty should take the entire screen, minus the gaps. @@ -43,8 +39,7 @@ static bool test() { auto kittyB = Tests::spawnKitty("kittyB"); if (!kittyB) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kittyB' })")); @@ -61,8 +56,7 @@ static bool test() { auto kittyC = Tests::spawnKitty("kittyC"); if (!kittyC) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Check kittyC dimensions", Colors::YELLOW); @@ -87,12 +81,11 @@ static bool test() { NLog::log("{}Spawning kittyProcA", Colors::YELLOW); auto kittyProcA = Tests::spawnKitty(); if (!kittyProcA) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Expecting 1 window", Colors::YELLOW); - EXPECT(Tests::windowCount(), 1); + ASSERT(Tests::windowCount(), 1); // check kitty properties. One kitty should take the entire screen, minus the gaps. NLog::log("{}Check kitty dimensions", Colors::YELLOW); @@ -127,8 +120,7 @@ static bool test() { NLog::log("{}Spawn kitty again", Colors::YELLOW); kittyProcA = Tests::spawnKitty(); if (!kittyProcA) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Group kitty", Colors::YELLOW); @@ -145,12 +137,11 @@ static bool test() { NLog::log("{}Spawn kittyProcB", Colors::YELLOW); auto kittyProcB = Tests::spawnKitty(); if (!kittyProcB) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Expecting 2 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); size_t lastActiveKittyIdx = 0; @@ -158,10 +149,7 @@ static bool test() { try { auto str = getFromSocket("/activewindow"); lastActiveKittyIdx = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } + } catch (...) { FAIL_TEST("Could not extract the active window id"); } // test cycling through @@ -170,21 +158,15 @@ static bool test() { try { auto str = getFromSocket("/activewindow"); - EXPECT(lastActiveKittyIdx != std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16), true); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } + ASSERT(lastActiveKittyIdx != std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16), true); + } catch (...) { FAIL_TEST("Could not extract the active window id"); } getFromSocket("/dispatch hl.dsp.group.next()"); try { auto str = getFromSocket("/activewindow"); - EXPECT(lastActiveKittyIdx, std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16)); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } + ASSERT(lastActiveKittyIdx, std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16)); + } catch (...) { FAIL_TEST("Could not extract the active window id"); } // test movegroupwindow: focus should follow the moved window NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW); @@ -195,10 +177,7 @@ static bool test() { str = getFromSocket("/activewindow"); auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); EXPECT(activeAfterMove, activeBeforeMove); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } + } catch (...) { FAIL_TEST("Could not extract the active window id"); } // and backwards NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW); @@ -209,10 +188,7 @@ static bool test() { str = getFromSocket("/activewindow"); auto activeAfterMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); EXPECT(activeAfterMove, activeBeforeMove); - } catch (...) { - NLog::log("{}Fail at getting prop", Colors::RED); - ret = 1; - } + } catch (...) { FAIL_TEST("Could not extract the active window id"); } NLog::log("{}Disable autogrouping", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ group = { auto_group = false } })")); @@ -220,12 +196,11 @@ static bool test() { NLog::log("{}Spawn kittyProcC", Colors::YELLOW); auto kittyProcC = Tests::spawnKitty(); if (!kittyProcC) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Expecting 3 windows 2", Colors::YELLOW); - EXPECT(Tests::windowCount(), 3); + ASSERT(Tests::windowCount(), 3); { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 2); @@ -239,12 +214,11 @@ static bool test() { NLog::log("{}Spawn kittyProcD", Colors::YELLOW); auto kittyProcD = Tests::spawnKitty(); if (!kittyProcD) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Expecting 4 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 4); + ASSERT(Tests::windowCount(), 4); OK(getFromSocket("/dispatch hl.dsp.group.active({ index = 3 })")); @@ -258,7 +232,7 @@ static bool test() { Tests::killAllWindows(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // test movewindoworgroup: direction should be respected when extracting from group NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW); @@ -266,8 +240,7 @@ static bool test() { { auto kittyE = Tests::spawnKitty(); if (!kittyE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } // group kitty, and new windows should be auto-grouped @@ -275,10 +248,9 @@ static bool test() { auto kittyF = Tests::spawnKitty(); if (!kittyF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); // both windows should be grouped at the same position { @@ -310,7 +282,7 @@ static bool test() { } Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } // test that we deny a floated window getting auto-grouped into a tiled group. @@ -320,8 +292,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'kitty-tiled', tile = true })")); auto kittyProcE = Tests::spawnKitty("kitty_tiled"); if (!kittyProcE) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); @@ -329,18 +300,16 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'kitty-floated', float = true })")); auto kittyProcF = Tests::spawnKitty("kitty_floated"); if (!kittyProcF) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); { auto clients = getFromSocket("/clients"); auto classPos = clients.find("class: kitty_floated"); if (classPos == std::string::npos) { - NLog::log("{}Could not find kitty_floated in clients output", Colors::RED); - ret = 1; + FAIL_TEST("Could not find kitty_floated in clients output"); } else { auto entryStart = clients.rfind("Window ", classPos); auto entryEnd = clients.find("\n\n", classPos); @@ -351,27 +320,25 @@ static bool test() { } Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // Tests for grouping/merging logic NLog::log("{}Testing locked groups w/ invade", Colors::GREEN); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // Test normal, unlocked groups { auto winA = Tests::spawnKitty("unlocked"); if (!winA) { - NLog::log("{}Error: unlocked kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn unlocked kitty"); } OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); auto winB = Tests::spawnKitty("top"); if (!winB) { - NLog::log("{}Error: top kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn top kitty"); } // Verify it DID merge into a group @@ -382,14 +349,13 @@ static bool test() { } Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // Test locked groups { auto lockedWin = Tests::spawnKitty("locked"); if (!lockedWin) { - NLog::log("{}Error: locked kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn locked kitty"); } OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); OK(getFromSocket(std::format("/dispatch hl.dsp.focus({{ window = 'pid:{}' }})", lockedWin->pid()))); @@ -397,8 +363,7 @@ static bool test() { auto winB = Tests::spawnKitty("top"); if (!winB) { - NLog::log("{}Error: top kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn top kitty"); } // Verify it did NOT merge into the locked group @@ -409,7 +374,7 @@ static bool test() { } Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); // Test locked groups WITH invade rule { @@ -418,14 +383,12 @@ static bool test() { auto lockedWin = Tests::spawnKitty("locked"); if (!lockedWin) { - NLog::log("{}Error: locked kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn locked kitty"); } auto invadingWin = Tests::spawnKitty("invade"); if (!invadingWin) { - NLog::log("{}Error: invading kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn invading kitty"); } // Verify it DID merge into the locked group @@ -434,9 +397,5 @@ static bool test() { } Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - return !ret; + ASSERT(Tests::windowCount(), 0); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/hyprctl.cpp b/hyprtester/src/tests/main/hyprctl.cpp index c6d6a9bf..c67c8af9 100644 --- a/hyprtester/src/tests/main/hyprctl.cpp +++ b/hyprtester/src/tests/main/hyprctl.cpp @@ -12,8 +12,6 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; @@ -35,9 +33,7 @@ static void setWindowProp(const std::string& selector, const std::string& prop, getFromSocket("/dispatch hl.dsp.window.set_prop({ window = '" + selector + "', prop = '" + prop + "', value = '" + value + "' })"); } -static bool testDevicesActiveLayoutIndex() { - NLog::log("{}Testing hyprctl devices active_layout_index", Colors::GREEN); - +TEST_CASE(hyprctlDevicesActiveLayoutIndex) { // configure layouts OK(getFromSocket("r/eval hl.config({ input = { kb_layout = \"us,pl,ua\" } })")); @@ -49,15 +45,11 @@ static bool testDevicesActiveLayoutIndex() { // check layout index EXPECT_CONTAINS(devicesJson, expected); } - - return true; } -static bool testGetprop() { - NLog::log("{}Testing hyprctl getprop", Colors::GREEN); +TEST_CASE(hyprctlGetprop) { if (!Tests::spawnKitty()) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } // animation @@ -166,41 +158,16 @@ static bool testGetprop() { EXPECT(getCommandStdOut("hyprctl getprop class:kitty"), "not enough args"); EXPECT(getCommandStdOut("hyprctl getprop class:nonexistantclass animation"), "window not found"); EXPECT(getCommandStdOut("hyprctl getprop class:kitty nonexistantprop"), "prop not found"); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; } -static void testSubmap() { - NLog::log("{}Testing hyprctl submap", Colors::GREEN); - +TEST_CASE(hyprctlSubmap) { EXPECT(getCommandStdOut("hyprctl submap"), "default\n"); EXPECT(getCommandStdOut("hyprctl submap -j | jq -r \".\""), "default"); } -static bool test() { - NLog::log("{}Testing hyprctl", Colors::GREEN); - - { - NLog::log("{}Testing hyprctl descriptions for any json errors", Colors::GREEN); - CProcess jqProc("bash", {"-c", "hyprctl descriptions | jq"}); - jqProc.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS); - jqProc.runSync(); - EXPECT(jqProc.exitCode(), 0); - } - - testGetprop(); - testDevicesActiveLayoutIndex(); - testSubmap(); - getFromSocket("/reload"); - - return !ret; +TEST_CASE(hyprctlJsonErrors) { + CProcess jqProc("bash", {"-c", "hyprctl descriptions | jq"}); + jqProc.addEnv("HYPRLAND_INSTANCE_SIGNATURE", HIS); + jqProc.runSync(); + EXPECT(jqProc.exitCode(), 0); } - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/keybinds.cpp b/hyprtester/src/tests/main/keybinds.cpp index c9a7786c..a1b5288c 100644 --- a/hyprtester/src/tests/main/keybinds.cpp +++ b/hyprtester/src/tests/main/keybinds.cpp @@ -8,8 +8,6 @@ using namespace Hyprutils::OS; using namespace Hyprutils::Memory; - -static int ret = 0; static std::string flagFile = "/tmp/hyprtester-keybinds.txt"; static std::string pluginKeybindCmd(bool pressed, uint32_t modifier, uint32_t key) { @@ -88,7 +86,11 @@ static CUniquePointer<CProcess> spawnRemoteControlKitty() { return kittyProc; } -static void testBind() { +// All the `SUBTEST`s below are supposed to be independent `TEST_CASE`s. +// But if isolated trivially, some of them fail. +// TODO: investigate and isolate tests by turning `SUBTEST`s into `TEST_CASE`s. + +SUBTEST(bind) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'))"), "ok"); // press keybind @@ -100,7 +102,7 @@ static void testBind() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testBindKey() { +SUBTEST(bindKey) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('Y', hl.dsp.exec_cmd('touch " + flagFile + "'))"), "ok"); // press keybind @@ -112,7 +114,7 @@ static void testBindKey() { EXPECT(getFromSocket("/eval hl.unbind('', 'Y')"), "ok"); } -static void testLongPress() { +SUBTEST(longPress) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { long_press = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -128,8 +130,7 @@ static void testLongPress() { OK(getFromSocket(pluginKeybindCmd(false, 0, 29))); EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } - -static void testKeyLongPress() { +SUBTEST(keyLongPress) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { long_press = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -146,7 +147,7 @@ static void testKeyLongPress() { EXPECT(getFromSocket("/eval hl.unbind('', 'Y')"), "ok"); } -static void testLongPressRelease() { +SUBTEST(longPressRelease) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { long_press = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -162,8 +163,7 @@ static void testLongPressRelease() { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } - -static void testLongPressOnlyKeyRelease() { +SUBTEST(longPressOnlyKeyRelease) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { long_press = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -181,7 +181,7 @@ static void testLongPressOnlyKeyRelease() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testRepeat() { +SUBTEST(repeat) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { repeating = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -201,7 +201,7 @@ static void testRepeat() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testKeyRepeat() { +SUBTEST(keyRepeat) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { repeating = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -221,7 +221,7 @@ static void testKeyRepeat() { EXPECT(getFromSocket("/eval hl.unbind('', 'Y')"), "ok"); } -static void testRepeatRelease() { +SUBTEST(repeatRelease) { // wait until flag becomes false (CI timing can vary) bool ok = false; for (int i = 0; i < 20; ++i) { @@ -253,7 +253,7 @@ static void testRepeatRelease() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testRepeatOnlyKeyRelease() { +SUBTEST(repeatOnlyKeyRelease) { EXPECT(checkFlag(), false); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.exec_cmd('touch " + flagFile + "'), { repeating = true })"), "ok"); EXPECT(getFromSocket("r/eval hl.config({ input = { repeat_delay = 100 } })"), "ok"); @@ -276,12 +276,10 @@ static void testRepeatOnlyKeyRelease() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testShortcutBind() { +SUBTEST(shortcutBind) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }))"), "ok"); @@ -298,12 +296,10 @@ static void testShortcutBind() { Tests::killAllWindows(); } -static void testShortcutBindKey() { +SUBTEST(shortcutBindKey) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }))"), "ok"); @@ -321,12 +317,10 @@ static void testShortcutBindKey() { Tests::killAllWindows(); } -static void testShortcutLongPress() { +SUBTEST(shortcutLongPress) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }), { long_press = true })"), "ok"); @@ -350,12 +344,10 @@ static void testShortcutLongPress() { Tests::killAllWindows(); } -static void testShortcutLongPressKeyRelease() { +SUBTEST(shortcutLongPressKeyRelease) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }), { long_press = true })"), "ok"); @@ -377,12 +369,10 @@ static void testShortcutLongPressKeyRelease() { Tests::killAllWindows(); } -static void testShortcutRepeat() { +SUBTEST(shortcutRepeat) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }), { repeating = true })"), "ok"); @@ -407,12 +397,10 @@ static void testShortcutRepeat() { Tests::killAllWindows(); } -static void testShortcutRepeatKeyRelease() { +SUBTEST(shortcutRepeatKeyRelease) { auto kittyProc = spawnRemoteControlKitty(); if (!kittyProc) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } EXPECT(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:keybinds_test' })"), "ok"); EXPECT(getFromSocket("/eval hl.bind('SUPER + Y', hl.dsp.send_shortcut({ mods = '', key = 'q', window = 'activewindow' }), { repeating = true })"), "ok"); @@ -439,7 +427,7 @@ static void testShortcutRepeatKeyRelease() { Tests::killAllWindows(); } -static void testSubmap() { +SUBTEST(submap) { const auto press = [](const uint32_t key, const uint32_t mod = 0) { // +8 because udev -> XKB keycode. getFromSocket(pluginKeybindCmd(true, mod, key + 8)); @@ -474,7 +462,7 @@ static void testSubmap() { Tests::killAllWindows(); } -static void testBindsAfterScroll() { +SUBTEST(bindsAfterScroll) { NLog::log("{}Testing binds after scroll", Colors::GREEN); clearFlag(); @@ -503,7 +491,7 @@ static void testBindsAfterScroll() { OK(getFromSocket("/eval hl.unbind('ALT', 'w')")); } -static void testSubmapUniversal() { +SUBTEST(submapUniversal) { NLog::log("{}Testing submap universal", Colors::GREEN); EXPECT(checkFlag(), false); @@ -531,7 +519,7 @@ static void testSubmapUniversal() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static void testPerDeviceKeybind() { +SUBTEST(perDeviceKeybind) { NLog::log("{}Testing per-device binds", Colors::GREEN); // Inclusive @@ -561,34 +549,26 @@ static void testPerDeviceKeybind() { EXPECT(getFromSocket("/eval hl.unbind('SUPER', 'Y')"), "ok"); } -static bool test() { - NLog::log("{}Testing keybinds", Colors::GREEN); - - clearFlag(); - - testBind(); - testBindKey(); - testLongPress(); - testKeyLongPress(); - testLongPressRelease(); - testLongPressOnlyKeyRelease(); - testRepeat(); - testKeyRepeat(); - testRepeatRelease(); - testRepeatOnlyKeyRelease(); - testShortcutBind(); - testShortcutBindKey(); - testShortcutLongPress(); - testShortcutLongPressKeyRelease(); - testShortcutRepeat(); - testShortcutRepeatKeyRelease(); - testSubmap(); - testSubmapUniversal(); - testBindsAfterScroll(); - testPerDeviceKeybind(); - - clearFlag(); - return !ret; +// TODO: remove this test after subtests above are properly isolated into independent tests +TEST_CASE(keybinds) { + CALL_SUBTEST(bind); + CALL_SUBTEST(bindKey); + CALL_SUBTEST(longPress); + CALL_SUBTEST(keyLongPress); + CALL_SUBTEST(longPressRelease); + CALL_SUBTEST(longPressOnlyKeyRelease); + CALL_SUBTEST(repeat); + CALL_SUBTEST(keyRepeat); + CALL_SUBTEST(repeatRelease); + CALL_SUBTEST(repeatOnlyKeyRelease); + CALL_SUBTEST(shortcutBind); + CALL_SUBTEST(shortcutBindKey); + CALL_SUBTEST(shortcutLongPress); + CALL_SUBTEST(shortcutLongPressKeyRelease); + CALL_SUBTEST(shortcutRepeat); + CALL_SUBTEST(shortcutRepeatKeyRelease); + CALL_SUBTEST(submap); + CALL_SUBTEST(submapUniversal); + CALL_SUBTEST(bindsAfterScroll); + CALL_SUBTEST(perDeviceKeybind); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/layer.cpp b/hyprtester/src/tests/main/layer.cpp index df74f8b7..f39c007c 100644 --- a/hyprtester/src/tests/main/layer.cpp +++ b/hyprtester/src/tests/main/layer.cpp @@ -6,8 +6,6 @@ #include <hyprutils/os/Process.hpp> #include <hyprutils/memory/WeakPtr.hpp> -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; @@ -20,34 +18,18 @@ static bool spawnLayer(const std::string& namespace_) { return true; } -static bool test() { - NLog::log("{}Testing plugin layerrules", Colors::GREEN); +TEST_CASE(plugin_layerrules) { - if (!spawnLayer("rule-layer")) - return false; + EXPECT(spawnLayer("rule-layer"), true); OK(getFromSocket("/eval hl.plugin.test.add_layer_rule()")); OK(getFromSocket("/reload")); OK(getFromSocket("/eval hl.layer_rule({ match = { namespace = 'rule-layer' }, plugin_rule = 'effect' })")); - if (!spawnLayer("rule-layer")) - return false; + EXPECT(spawnLayer("rule-layer"), true); - if (!spawnLayer("norule-layer")) - return false; + EXPECT(spawnLayer("norule-layer"), true); OK(getFromSocket("/eval hl.plugin.test.check_layer_rule()")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all layers", Colors::YELLOW); - Tests::killAllLayers(); - - NLog::log("{}Expecting 0 layers", Colors::YELLOW); - EXPECT(Tests::layerCount(), 0); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/layout.cpp b/hyprtester/src/tests/main/layout.cpp index e2336add..4d604fa4 100644 --- a/hyprtester/src/tests/main/layout.cpp +++ b/hyprtester/src/tests/main/layout.cpp @@ -3,9 +3,7 @@ #include "../../hyprctlCompat.hpp" #include "tests.hpp" -static int ret = 0; - -static void swar() { +TEST_CASE(single_window_aspect_ratio) { OK(getFromSocket("/eval hl.config({ layout = { single_window_aspect_ratio = '1 1' } })")); Tests::spawnKitty(); @@ -36,14 +34,10 @@ static void swar() { EXPECT_CONTAINS(str, "at: 22,22"); EXPECT_CONTAINS(str, "size: 1876,1036"); } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } // Don't crash when focus after global geometry changes -static void testCrashOnGeomUpdate() { +TEST_CASE(crashOnGeomUpdate) { Tests::spawnKitty(); Tests::spawnKitty(); Tests::spawnKitty(); @@ -53,15 +47,10 @@ static void testCrashOnGeomUpdate() { // shouldnt crash OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'right' })")); - - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } // Test if size + pos is preserved after fs cycle -static void testPosPreserve() { +TEST_CASE(posPreserve) { Tests::spawnKitty(); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set', window = 'class:kitty' })")); @@ -98,21 +87,18 @@ static void testPosPreserve() { EXPECT_CONTAINS(str, "at: 581,420"); EXPECT_CONTAINS(str, "size: 1337,69"); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static bool testFocusMRUAfterClose() { +TEST_CASE(focusMRUAfterClose) { NLog::log("{}Testing focus after close (MRU order)", Colors::GREEN); OK(getFromSocket("/reload")); OK(getFromSocket("/eval hl.config({ dwindle = { default_split_ratio = 1.25 } })")); OK(getFromSocket("/eval hl.config({ input = { focus_on_close = 2 } })")); - EXPECT(!!Tests::spawnKitty("kitty_A"), true); - EXPECT(!!Tests::spawnKitty("kitty_B"), true); - EXPECT(!!Tests::spawnKitty("kitty_C"), true); + ASSERT(!!Tests::spawnKitty("kitty_A"), true); + ASSERT(!!Tests::spawnKitty("kitty_B"), true); + ASSERT(!!Tests::spawnKitty("kitty_C"), true); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })")); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_B' })")); @@ -133,22 +119,15 @@ static bool testFocusMRUAfterClose() { auto str = getFromSocket("/activewindow"); EXPECT(str.contains("class: kitty_A"), true); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return true; } -static bool testFocusPreservedLayoutChange() { - NLog::log("{}Testing focus is preserved on layout change", Colors::GREEN); - +TEST_CASE(focusPreservedLayoutChange) { OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })")); - EXPECT(!!Tests::spawnKitty("kitty_A"), true); - EXPECT(!!Tests::spawnKitty("kitty_B"), true); - EXPECT(!!Tests::spawnKitty("kitty_C"), true); - EXPECT(!!Tests::spawnKitty("kitty_D"), true); + ASSERT(!!Tests::spawnKitty("kitty_A"), true); + ASSERT(!!Tests::spawnKitty("kitty_B"), true); + ASSERT(!!Tests::spawnKitty("kitty_C"), true); + ASSERT(!!Tests::spawnKitty("kitty_D"), true); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_C' })")); @@ -158,34 +137,4 @@ static bool testFocusPreservedLayoutChange() { auto str = getFromSocket("/activewindow"); EXPECT(str.contains("class: kitty_C"), true); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return true; } - -static bool test() { - NLog::log("{}Testing layout generic", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '10' })")); - - // test - NLog::log("{}Testing `single_window_aspect_ratio`", Colors::GREEN); - swar(); - - testCrashOnGeomUpdate(); - testPosPreserve(); - testFocusMRUAfterClose(); - testFocusPreservedLayoutChange(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/master.cpp b/hyprtester/src/tests/main/master.cpp index 02792631..b9364c45 100644 --- a/hyprtester/src/tests/main/master.cpp +++ b/hyprtester/src/tests/main/master.cpp @@ -3,10 +3,44 @@ #include "../../hyprctlCompat.hpp" #include "tests.hpp" -static int ret = 0; +TEST_CASE(focusMasterPrevious) { + OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })")); + + // setup + NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW); + // order of windows set according to new_status = master (set in test.lua) + for (auto const& win : {"slave1", "slave2", "slave3", "master"}) { + if (!Tests::spawnKitty(win)) { + FAIL_TEST("Could not spawn kitty with win class `{}`", win); + } + } + NLog::log("{}Ensuring focus is on master before testing", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster master')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: master"); + + // test + NLog::log("{}Testing fallback to focusmaster auto", Colors::YELLOW); + + OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: slave1"); + + NLog::log("{}Testing focusing from slave to master", Colors::YELLOW); + + OK(getFromSocket("/dispatch hl.dsp.layout('cyclenext noloop')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); + OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: master"); + + NLog::log("{}Testing focusing on previous window", Colors::YELLOW); + + OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); + + NLog::log("{}Testing focusing back to master", Colors::YELLOW); + + OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: master"); -// reqs 1 master 3 slaves -static void testOrientations() { OK(getFromSocket("r/eval hl.config({ master = { orientation = 'top' } })")); // top @@ -51,62 +85,15 @@ static void testOrientations() { } } -static void focusMasterPrevious() { - // setup - NLog::log("{}Spawning 1 master and 3 slave windows", Colors::YELLOW); - // order of windows set according to new_status = master (set in test.lua) - for (auto const& win : {"slave1", "slave2", "slave3", "master"}) { - if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; - } - } - NLog::log("{}Ensuring focus is on master before testing", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster master')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - // test - NLog::log("{}Testing fallback to focusmaster auto", Colors::YELLOW); - - OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave1"); - - NLog::log("{}Testing focusing from slave to master", Colors::YELLOW); - - OK(getFromSocket("/dispatch hl.dsp.layout('cyclenext noloop')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); - OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - NLog::log("{}Testing focusing on previous window", Colors::YELLOW); - - OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: slave2"); - - NLog::log("{}Testing focusing back to master", Colors::YELLOW); - - OK(getFromSocket("/dispatch hl.dsp.layout('focusmaster previous')")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: master"); - - testOrientations(); - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static void testFsBehavior() { +TEST_CASE(fsBehavior) { // Master will re-send data to fullscreen / maximized windows, which can interfere with misc:on_focus_under_fullscreen // check that it doesn't. + OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })")); + for (auto const& win : {"master", "slave1", "slave2"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -153,31 +140,4 @@ static void testFsBehavior() { EXPECT_CONTAINS(str, "class: vaxwashere"); EXPECT_CONTAINS(str, "fullscreen: 0"); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); -} - -static bool test() { - NLog::log("{}Testing Master layout", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:master' })")); - OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })")); - - // test - NLog::log("{}Testing `focusmaster previous` layoutmsg", Colors::GREEN); - focusMasterPrevious(); - - NLog::log("{}Testing fs behavior", Colors::GREEN); - testFsBehavior(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/misc.cpp b/hyprtester/src/tests/main/misc.cpp index 704ba9d1..24cc4097 100644 --- a/hyprtester/src/tests/main/misc.cpp +++ b/hyprtester/src/tests/main/misc.cpp @@ -10,8 +10,6 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; @@ -38,7 +36,7 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "workspace: 2"); +// ASSERT_CONTAINS(str, "workspace: 2"); // } // // OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); @@ -48,13 +46,13 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); +// ASSERT_CONTAINS(str, "windows: 0"); // } // // { // OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:hyprland-dialog' })")) // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "workspace: 2"); +// ASSERT_CONTAINS(str, "workspace: 2"); // } // } // @@ -73,7 +71,7 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "yummy"); +// ASSERT_CONTAINS(str, "yummy"); // } // // OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); @@ -83,13 +81,13 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); +// ASSERT_CONTAINS(str, "windows: 0"); // } // // { // OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:hyprland-dialog' })")) // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "yummy"); +// ASSERT_CONTAINS(str, "yummy"); // } // } // @@ -108,7 +106,7 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "special:apple"); +// ASSERT_CONTAINS(str, "special:apple"); // } // // OK(getFromSocket("/dispatch hl.dsp.workspace.toggle_special('apple')")); @@ -119,13 +117,13 @@ using namespace Hyprutils::Memory; // // { // auto str = getFromSocket("/activeworkspace"); -// EXPECT_CONTAINS(str, "windows: 0"); +// ASSERT_CONTAINS(str, "windows: 0"); // } // // { // OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:hyprland-dialog' })")) // auto str = getFromSocket("/activewindow"); -// EXPECT_CONTAINS(str, "special:apple"); +// ASSERT_CONTAINS(str, "special:apple"); // } // } // @@ -133,9 +131,8 @@ using namespace Hyprutils::Memory; // Tests::killAllWindows(); // } -static bool test() { - NLog::log("{}Testing config: misc:", Colors::GREEN); - +// TODO: decompose this into multiple test cases +TEST_CASE(misc) { NLog::log("{}Testing close_special_on_empty", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ misc = { close_special_on_empty = false } })")); @@ -145,14 +142,14 @@ static bool test() { { auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "special workspace: -"); + ASSERT_CONTAINS(str, "special workspace: -"); } Tests::killAllWindows(); { auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "special workspace: -"); + ASSERT_CONTAINS(str, "special workspace: -"); } Tests::spawnKitty(); @@ -163,7 +160,7 @@ static bool test() { { auto str = getFromSocket("/monitors"); - EXPECT_NOT_CONTAINS(str, "special workspace: -"); + ASSERT_NOT_CONTAINS(str, "special workspace: -"); } NLog::log("{}Testing new_window_takes_over_fullscreen", Colors::YELLOW); @@ -350,19 +347,10 @@ static bool test() { EXPECT_CONTAINS(str, "fullscreen: 2"); EXPECT_CONTAINS(str, "fullscreenClient: 2"); } +} +TEST_CASE(processesThatDieEarlyAreReaped) { // Ensure that the process autostarted in the config does not // become a zombie even if it terminates very quickly. - EXPECT(Tests::execAndGet("pgrep -f 'sleep 0'").empty(), true); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; + ASSERT(Tests::execAndGet("pgrep -f 'sleep 0'").empty(), true); } - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/moveintoorcreategroup.cpp b/hyprtester/src/tests/main/moveintoorcreategroup.cpp index 2d47661b..8c2f3aae 100644 --- a/hyprtester/src/tests/main/moveintoorcreategroup.cpp +++ b/hyprtester/src/tests/main/moveintoorcreategroup.cpp @@ -10,17 +10,13 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer -static bool test() { - NLog::log("{}Testing moveintoorcreategroup", Colors::GREEN); - +TEST_CASE(moveIntoOrCreateGroup) { NLog::log("{}Dispatching workspace `moveintoorcreategroup`", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:moveintoorcreategroup' })"); @@ -29,19 +25,17 @@ static bool test() { NLog::log("{}Spawning kittyA", Colors::YELLOW); auto kittyA = Tests::spawnKitty("kitty_A"); if (!kittyA) { - NLog::log("{}Error: kittyA did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty_A"); } NLog::log("{}Spawning kittyB", Colors::YELLOW); auto kittyB = Tests::spawnKitty("kitty_B"); if (!kittyB) { - NLog::log("{}Error: kittyB did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty_B"); } NLog::log("{}Expecting 2 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); { auto str = getFromSocket("/clients"); @@ -68,7 +62,7 @@ static bool test() { Tests::killAllWindows(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); NLog::log("{}Testing moveintoorcreategroup into existing group", Colors::YELLOW); @@ -80,7 +74,7 @@ static bool test() { auto kittyE = Tests::spawnKitty("kitty_E"); NLog::log("{}Expecting 3 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 3); + ASSERT(Tests::windowCount(), 3); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_D' })")); OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); @@ -100,12 +94,4 @@ static bool test() { auto str = getFromSocket("/activewindow"); EXPECT_CONTAINS(str, "kitty_E"); } - - NLog::log("{}Kill windows", Colors::YELLOW); - Tests::killAllWindows(); - OK(getFromSocket("/eval hl.config({ group = { auto_group = true } })")); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/persistent.cpp b/hyprtester/src/tests/main/persistent.cpp index 0a2a7f2e..a203b1f2 100644 --- a/hyprtester/src/tests/main/persistent.cpp +++ b/hyprtester/src/tests/main/persistent.cpp @@ -10,19 +10,13 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer -static bool test() { - NLog::log("{}Testing persistent workspaces", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - +TEST_CASE(persistentWorkspaces) { // test on workspace "window" NLog::log("{}Switching to workspace 1", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); // no OK: we might be on 1 already @@ -42,7 +36,7 @@ static bool test() { { auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "HEADLESS-PERSISTENT-TEST"); + ASSERT_CONTAINS(str, "HEADLESS-PERSISTENT-TEST"); } OK(getFromSocket("/dispatch hl.dsp.focus({ monitor = 'HEADLESS-PERSISTENT-TEST' })")); @@ -68,18 +62,4 @@ static bool test() { } OK(getFromSocket("/output remove HEADLESS-PERSISTENT-TEST")); - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - // reload cfg - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/scroll.cpp b/hyprtester/src/tests/main/scroll.cpp index 3b73b906..bd33e345 100644 --- a/hyprtester/src/tests/main/scroll.cpp +++ b/hyprtester/src/tests/main/scroll.cpp @@ -3,15 +3,12 @@ #include "../../hyprctlCompat.hpp" #include "tests.hpp" -static int ret = 0; +TEST_CASE(scrollFocusCycling) { + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); -static void testFocusCycling() { for (auto const& win : {"a", "b", "c", "d"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -21,49 +18,44 @@ static void testFocusCycling() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'right' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); + ASSERT_CONTAINS(str, "class: c"); } OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'right' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); + ASSERT_CONTAINS(str, "class: d"); } OK(getFromSocket("/dispatch hl.dsp.window.move({ direction = 'left' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); + ASSERT_CONTAINS(str, "class: d"); } OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'up' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); + ASSERT_CONTAINS(str, "class: c"); } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static void testFocusWrapping() { +TEST_CASE(scrollFocusWrapping) { + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + for (auto const& win : {"a", "b", "c", "d"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -76,14 +68,14 @@ static void testFocusWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); + ASSERT_CONTAINS(str, "class: d"); } OK(getFromSocket("/dispatch hl.dsp.layout('focus r')")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); + ASSERT_CONTAINS(str, "class: a"); } // set wrap_focus to false @@ -95,7 +87,7 @@ static void testFocusWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: a"); + ASSERT_CONTAINS(str, "class: a"); } OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:d' })")); @@ -104,21 +96,16 @@ static void testFocusWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: d"); + ASSERT_CONTAINS(str, "class: d"); } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static void testSwapcolWrapping() { +TEST_CASE(scrollSwapcolWrapping) { + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + for (auto const& win : {"a", "b", "c", "d"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -132,7 +119,7 @@ static void testSwapcolWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); + ASSERT_CONTAINS(str, "class: c"); } // clean up @@ -141,10 +128,7 @@ static void testSwapcolWrapping() { for (auto const& win : {"a", "b", "c", "d"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -154,7 +138,7 @@ static void testSwapcolWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } // clean up @@ -163,10 +147,7 @@ static void testSwapcolWrapping() { for (auto const& win : {"a", "b", "c", "d"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -180,7 +161,7 @@ static void testSwapcolWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:d' })")); @@ -190,15 +171,13 @@ static void testSwapcolWrapping() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); + ASSERT_CONTAINS(str, "class: c"); } - - // clean up - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); } -static bool testWindowRule() { +TEST_CASE(scrollWindowRule) { + OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); + NLog::log("{}Testing Scrolling Width", Colors::GREEN); // inject a new rule. @@ -206,53 +185,15 @@ static bool testWindowRule() { OK(getFromSocket("/eval hl.window_rule({ name = 'scrolling-width', scrolling_width = 0.1 })")); if (!Tests::spawnKitty("kitty_scroll")) { - NLog::log("{}Failed to spawn kitty with win class `kitty_scroll`", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty with win class `kitty_scroll`"); } if (!Tests::spawnKitty("kitty_scroll")) { - NLog::log("{}Failed to spawn kitty with win class `kitty_scroll`", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty with win class `kitty_scroll`"); } - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); // not the greatest test, but as long as res and gaps don't change, we good. EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 174,1036"); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return true; } - -static bool test() { - NLog::log("{}Testing Scroll layout", Colors::GREEN); - - // setup - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:scroll' })")); - OK(getFromSocket("r/eval hl.config({ general = { layout = 'scrolling' } })")); - - // test - NLog::log("{}Testing focus cycling", Colors::GREEN); - testFocusCycling(); - - // test - NLog::log("{}Testing focus wrap", Colors::GREEN); - testFocusWrapping(); - - // test - NLog::log("{}Testing swapcol wrap", Colors::GREEN); - testSwapcolWrapping(); - - testWindowRule(); - - // clean up - NLog::log("Cleaning up", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); - OK(getFromSocket("/reload")); - - return !ret; -} - -REGISTER_TEST_FN(test); diff --git a/hyprtester/src/tests/main/snap.cpp b/hyprtester/src/tests/main/snap.cpp index ca103fa0..0e0c4604 100644 --- a/hyprtester/src/tests/main/snap.cpp +++ b/hyprtester/src/tests/main/snap.cpp @@ -9,43 +9,35 @@ using Hyprutils::Math::Vector2D; -static int ret = 0; - static bool spawnFloatingKitty() { if (!Tests::spawnKitty()) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } - OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); - OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 100, y = 100 })")); - return true; -} - -static void expectSocket(const std::string& CMD) { - if (const auto RESULT = getFromSocket(CMD); RESULT != "ok") { - NLog::log("{}Failed: {}getFromSocket({}), expected ok, got {}. Source: {}@{}.", Colors::RED, Colors::RESET, CMD, RESULT, __FILE__, __LINE__); - ret = 1; - TESTS_FAILED++; - } else { - NLog::log("{}Passed: {}getFromSocket({}). Got ok", Colors::GREEN, Colors::RESET, CMD); - TESTS_PASSED++; - } + bool ok = true; + ok &= getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })") == "ok"; + ok &= getFromSocket("/dispatch hl.dsp.window.resize({ x = 100, y = 100 })") == "ok"; + return ok; } -static void expectSnapMove(const Vector2D FROM, const Vector2D* TO) { - const Vector2D& A = FROM; - const Vector2D& B = TO ? *TO : FROM; - if (TO) - NLog::log("{}Expecting snap to ({},{}) when window is moved to ({},{})", Colors::YELLOW, B.x, B.y, A.x, A.y); +SUBTEST(expectSnapMove, double fromX, double fromY, double toX, double toY) { + const Vector2D FROM = {fromX, fromY}; + const Vector2D TO = {toX, toY}; + if (FROM == TO) + NLog::log("{}Expecting no snap when window is moved to ({},{})", Colors::YELLOW, FROM.x, FROM.y); else - NLog::log("{}Expecting no snap when window is moved to ({},{})", Colors::YELLOW, A.x, A.y); + NLog::log("{}Expecting snap to ({},{}) when window is moved to ({},{})", Colors::YELLOW, TO.x, TO.y, FROM.x, FROM.y); - expectSocket(std::format("/dispatch hl.dsp.window.move({{ x = {}, y = {} }})", A.x, A.y)); - expectSocket("/eval hl.plugin.test.snapmove()"); - EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("at: {},{}", B.x, B.y)); + OK(getFromSocket(std::format("/dispatch hl.dsp.window.move({{ x = {}, y = {} }})", FROM.x, FROM.y))); + OK(getFromSocket("/eval hl.plugin.test.snapmove()")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("at: {},{}", TO.x, TO.y)); } -static void testWindowSnap(const bool RESPECTGAPS) { +SUBTEST(expectNoSnapMove, double x, double y) { + CALL_SUBTEST(expectSnapMove, x, y, x, y); +} + +SUBTEST(testWindowSnap, const bool RESPECTGAPS) { const int BORDERSIZE = 2; const int WINDOWSIZE = 100; @@ -55,22 +47,19 @@ static void testWindowSnap(const bool RESPECTGAPS) { const int GAP = (RESPECTGAPS ? 2 * GAPSIN : 0) + (2 * BORDERSIZE); const int END = GAP + WINDOWSIZE; - int x; - Vector2D predict; - - x = WINDOWGAP + END; - expectSnapMove({OTHER + x, OTHER}, nullptr); - expectSnapMove({OTHER - x, OTHER}, nullptr); - expectSnapMove({OTHER, OTHER + x}, nullptr); - expectSnapMove({OTHER, OTHER - x}, nullptr); + int x = WINDOWGAP + END; + CALL_SUBTEST(expectNoSnapMove, OTHER + x, OTHER); + CALL_SUBTEST(expectNoSnapMove, OTHER - x, OTHER); + CALL_SUBTEST(expectNoSnapMove, OTHER, OTHER + x); + CALL_SUBTEST(expectNoSnapMove, OTHER, OTHER - x); x -= 1; - expectSnapMove({OTHER + x, OTHER}, &(predict = {OTHER + END, OTHER})); - expectSnapMove({OTHER - x, OTHER}, &(predict = {OTHER - END, OTHER})); - expectSnapMove({OTHER, OTHER + x}, &(predict = {OTHER, OTHER + END})); - expectSnapMove({OTHER, OTHER - x}, &(predict = {OTHER, OTHER - END})); + CALL_SUBTEST(expectSnapMove, OTHER + x, OTHER, OTHER + END, OTHER); + CALL_SUBTEST(expectSnapMove, OTHER - x, OTHER, OTHER - END, OTHER); + CALL_SUBTEST(expectSnapMove, OTHER, OTHER + x, OTHER, OTHER + END); + CALL_SUBTEST(expectSnapMove, OTHER, OTHER - x, OTHER, OTHER - END); } -static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) { +SUBTEST(testMonitorSnap, const bool RESPECTGAPS, const bool OVERLAP) { const int BORDERSIZE = 2; const int WINDOWSIZE = 100; @@ -84,14 +73,14 @@ static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) { Vector2D predict; x = MONITORGAP + GAP; - expectSnapMove({x, x}, nullptr); + CALL_SUBTEST(expectNoSnapMove, x, x); x -= 1; - expectSnapMove({x, x}, &(predict = {GAP, GAP})); + CALL_SUBTEST(expectSnapMove, x, x, GAP, GAP); x = MONITORGAP + END; - expectSnapMove({1920 - x, 1080 - x}, nullptr); + CALL_SUBTEST(expectNoSnapMove, 1920 - x, 1080 - x); x -= 1; - expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - END, 1080 - END})); + CALL_SUBTEST(expectSnapMove, 1920 - x, 1080 - x, 1920 - END, 1080 - END); // test reserved area const int RESERVED = 200; @@ -99,17 +88,18 @@ static void testMonitorSnap(const bool RESPECTGAPS, const bool OVERLAP) { const int REND = RGAP + WINDOWSIZE; x = MONITORGAP + RGAP; - expectSnapMove({x, x}, nullptr); + CALL_SUBTEST(expectNoSnapMove, x, x); x -= 1; - expectSnapMove({x, x}, &(predict = {RGAP, RGAP})); + CALL_SUBTEST(expectSnapMove, x, x, RGAP, RGAP); x = MONITORGAP + REND; - expectSnapMove({1920 - x, 1080 - x}, nullptr); + CALL_SUBTEST(expectNoSnapMove, 1920 - x, 1080 - x); x -= 1; - expectSnapMove({1920 - x, 1080 - x}, &(predict = {1920 - REND, 1080 - REND})); + CALL_SUBTEST(expectSnapMove, 1920 - x, 1080 - x, 1920 - REND, 1080 - REND); } -static bool test() { +// TODO: decompose this into multiple test cases +TEST_CASE(snap) { NLog::log("{}Testing snap", Colors::GREEN); // move to monitor HEADLESS-2 @@ -125,10 +115,10 @@ static bool test() { // spawn a kitty terminal and move to (500,500) NLog::log("{}Spawning kittyProcA", Colors::YELLOW); if (!spawnFloatingKitty()) - return false; + FAIL_TEST("Could not spawn kitty"); NLog::log("{}Expecting 1 window", Colors::YELLOW); - EXPECT(Tests::windowCount(), 1); + ASSERT(Tests::windowCount(), 1); NLog::log("{}Move the kitty window to (500,500)", Colors::YELLOW); OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 500, y = 500 })")); @@ -136,41 +126,26 @@ static bool test() { // spawn a second kitty terminal NLog::log("{}Spawning kittyProcB", Colors::YELLOW); if (!spawnFloatingKitty()) - return false; + FAIL_TEST("Could not spawn kitty"); NLog::log("{}Expecting 2 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); NLog::log(""); - testWindowSnap(false); - testMonitorSnap(false, false); + CALL_SUBTEST(testWindowSnap, false); + CALL_SUBTEST(testMonitorSnap, false, false); NLog::log("\n{}Turning on respect_gaps", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ general = { snap = { respect_gaps = true } } })")); - testWindowSnap(true); - testMonitorSnap(true, false); + CALL_SUBTEST(testWindowSnap, true); + CALL_SUBTEST(testMonitorSnap, true, false); NLog::log("\n{}Turning on border_overlap", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ general = { snap = { respect_gaps = false } } })")); OK(getFromSocket("/eval hl.config({ general = { snap = { border_overlap = true } } })")); - testMonitorSnap(false, true); + CALL_SUBTEST(testMonitorSnap, false, true); NLog::log("\n{}Turning on both border_overlap and respect_gaps", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ general = { snap = { respect_gaps = true } } })")); - testMonitorSnap(true, true); - - // kill all - NLog::log("\n{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); - - return !ret; + CALL_SUBTEST(testMonitorSnap, true, true); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/solitary.cpp b/hyprtester/src/tests/main/solitary.cpp index b89c1090..3e124a1e 100644 --- a/hyprtester/src/tests/main/solitary.cpp +++ b/hyprtester/src/tests/main/solitary.cpp @@ -7,17 +7,13 @@ #include <hyprutils/memory/WeakPtr.hpp> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer -static bool test() { - NLog::log("{}Testing solitary clients", Colors::GREEN); - +TEST_CASE(solitaryClients) { OK(getFromSocket("/eval hl.config({ general = { allow_tearing = false } })")); OK(getFromSocket("/eval hl.config({ render = { direct_scanout = 0 } })")); OK(getFromSocket("/eval hl.config({ cursor = { no_hardware_cursors = 1 } })")); @@ -50,27 +46,16 @@ static bool test() { // NLog::log("{}Expecting kitty to almost pass for solitary/DS/tearing", Colors::YELLOW); // { // auto str = getFromSocket("/monitors"); - // EXPECT_NOT_CONTAINS(str, "solitary: 0\n"); - // EXPECT_CONTAINS(str, "solitaryBlockedBy: null"); - // EXPECT_CONTAINS(str, "activelyTearing: false"); - // EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor,window settings"); + // ASSERT_NOT_CONTAINS(str, "solitary: 0\n"); + // ASSERT_CONTAINS(str, "solitaryBlockedBy: null"); + // ASSERT_CONTAINS(str, "activelyTearing: false"); + // ASSERT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor,window settings"); // } // OK(getFromSocket("/dispatch hl.dsp.window.set_prop({ window = 'active', prop = 'immediate', value = '1' })")); // NLog::log("{}Expecting kitty to almost pass for tearing", Colors::YELLOW); // { // auto str = getFromSocket("/monitors"); - // EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor\n"); + // ASSERT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,not supported by monitor\n"); // } - - // // kill all - // NLog::log("{}Killing all windows", Colors::YELLOW); - // Tests::killAllWindows(); - - NLog::log("{}Reloading the config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/tags.cpp b/hyprtester/src/tests/main/tags.cpp index 45ee4088..d83f00fa 100644 --- a/hyprtester/src/tests/main/tags.cpp +++ b/hyprtester/src/tests/main/tags.cpp @@ -3,20 +3,13 @@ #include "../shared.hpp" #include "tests.hpp" -static int ret = 0; - -static bool testTags() { - NLog::log("{}Testing tags", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - +TEST_CASE(tags) { NLog::log("{}Spawning kittyProcA&B on ws 1", Colors::YELLOW); auto kittyProcA = Tests::spawnKitty("tagged"); auto kittyProcB = Tests::spawnKitty("untagged"); if (!kittyProcA || !kittyProcB) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Testing testTag tags", Colors::YELLOW); @@ -28,7 +21,7 @@ static bool testTags() { OK(getFromSocket("/eval hl.window_rule({ name = 'tag-test-3', match = { tag = 'testTag' } })")); OK(getFromSocket("/eval hl.window_rule({ name = 'tag-test-3', no_dim = true })")); - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:tagged' })")); NLog::log("{}Testing tagged window for no_dim 0 & no_shadow", Colors::YELLOW); EXPECT_CONTAINS(getFromSocket("/activewindow"), "testTag"); @@ -39,13 +32,4 @@ static bool testTags() { EXPECT_NOT_CONTAINS(getFromSocket("/activewindow"), "testTag"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_shadow"), "true"); EXPECT_CONTAINS(getFromSocket("/getprop activewindow no_dim"), "false"); - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - - OK(getFromSocket("/reload")); - - return ret == 0; } - -REGISTER_TEST_FN(testTags) diff --git a/hyprtester/src/tests/main/tests.hpp b/hyprtester/src/tests/main/tests.hpp index 757052b6..7ee4fd4a 100644 --- a/hyprtester/src/tests/main/tests.hpp +++ b/hyprtester/src/tests/main/tests.hpp @@ -1,12 +1,9 @@ #pragma once +#include <map> -#include <vector> -#include <functional> +#include "../shared.hpp" -inline std::vector<std::function<bool()>> testFns; +inline std::map<const char*, CTestCase&> mainTestCases; -#define REGISTER_TEST_FN(fn) \ - static auto _register_fn = [] { \ - testFns.emplace_back(fn); \ - return 1; \ - }(); +// Where `TEST_CASE` macros will store generated test cases: +#define TEST_CASES_STORAGE mainTestCases diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp index f8e7c499..730c51be 100644 --- a/hyprtester/src/tests/main/window.cpp +++ b/hyprtester/src/tests/main/window.cpp @@ -14,8 +14,7 @@ #include "../shared.hpp" #include "tests.hpp" -static int ret = 0; - +// TODO: seems redundant, can just use `Tests::spawnKitty`? static bool spawnKitty(const std::string& class_, const std::vector<std::string>& args = {}) { NLog::log("{}Spawning {}", Colors::YELLOW, class_); if (!Tests::spawnKitty(class_, args)) { @@ -52,31 +51,26 @@ static std::string getWindowAddress(const std::string& winInfo) { auto pos2 = winInfo.find(" -> "); if (pos == std::string::npos || pos2 == std::string::npos) { NLog::log("{}Wrong window info", Colors::RED); - ret = 1; return "Wrong window info"; } return winInfo.substr(pos + 7, pos2 - pos - 7); } -static void testSwapWindow() { - NLog::log("{}Testing swapwindow", Colors::GREEN); - +TEST_CASE(swapWindow) { // test on workspace "swapwindow" NLog::log("{}Switching to workspace \"swapwindow\"", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:swapwindow' })"); if (!Tests::spawnKitty("kitty_A")) { - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } if (!Tests::spawnKitty("kitty_B")) { - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } NLog::log("{}Expecting 2 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 2); + ASSERT(Tests::windowCount(), 2); // Test swapwindow by direction { @@ -138,18 +132,9 @@ static void testSwapWindow() { getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_B' })"); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("{}", ws)); } - - // kill all - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); } -static void testGroupRules() { - NLog::log("{}Testing group window rules", Colors::YELLOW); - +TEST_CASE(windowGroupRules) { OK(getFromSocket("/eval hl.config({ general = { border_size = 8 } })")); OK(getFromSocket("/eval hl.workspace_rule({ workspace = 'w[tv1]', border_size = 0 })")); OK(getFromSocket("/eval hl.workspace_rule({ workspace = 'f[1]', border_size = 0 })")); @@ -157,8 +142,7 @@ static void testGroupRules() { OK(getFromSocket("/eval hl.window_rule({ match = { workspace = 'f[1]' }, border_size = 0 })")); if (!Tests::spawnKitty("kitty_A")) { - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } { @@ -167,8 +151,7 @@ static void testGroupRules() { } if (!Tests::spawnKitty("kitty_B")) { - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } { @@ -194,8 +177,7 @@ static void testGroupRules() { } if (!Tests::spawnKitty("kitty_C")) { - ret = 1; - return; + FAIL_TEST("Could not spawn kitty"); } OK(getFromSocket("/dispatch hl.dsp.window.move({ out_of_group = 'right' })")); @@ -204,9 +186,6 @@ static void testGroupRules() { auto str = getFromSocket("/getprop active border_size"); EXPECT_CONTAINS(str, "8"); } - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); } static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) { @@ -236,11 +215,11 @@ static bool waitForActiveWindow(const std::string& class_, char fullscreen = '0' /// Tests behavior of a window being focused when on that window's workspace /// another fullscreen window exists. -static bool testWindowFocusOnFullscreenConflict() { +TEST_CASE(windowFocusOnFullscreenConflict) { if (!spawnKitty("kitty_A")) - return false; + FAIL_TEST("Could not spawn kitty"); if (!spawnKitty("kitty_B")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/eval hl.config({ misc = { focus_on_activate = true } })")); @@ -263,7 +242,7 @@ static bool testWindowFocusOnFullscreenConflict() { // Make a window that will request focus const std::string removeToActivate = spawnKittyActivating(); if (removeToActivate.empty()) - return false; + FAIL_TEST("Could not spawn kitty_activating"); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })")); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set' })")); EXPECT(isActiveWindow("kitty_A", '2'), true); @@ -293,7 +272,7 @@ static bool testWindowFocusOnFullscreenConflict() { // Make a window that will request focus const std::string removeToActivate = spawnKittyActivating(); if (removeToActivate.empty()) - return false; + FAIL_TEST("Could not spawn kitty_activating"); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })")); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set' })")); EXPECT(isActiveWindow("kitty_A", '2'), true); @@ -318,30 +297,17 @@ static bool testWindowFocusOnFullscreenConflict() { // Make a window that will request focus - the setting is treated normally const std::string removeToActivate = spawnKittyActivating(); if (removeToActivate.empty()) - return false; + FAIL_TEST("Could not spawn kitty_activating"); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })")); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set' })")); EXPECT(isActiveWindow("kitty_A", '2'), true); std::filesystem::remove(removeToActivate); EXPECT(waitForActiveWindow("kitty_A", '2'), true); } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; } -static void testMaximizeSize() { - NLog::log("{}Testing maximize size", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); +TEST_CASE(windowMaximizeSize) { + ASSERT(spawnKitty("kitty_A"), true); // check kitty properties. Maximizing shouldnt change its size { @@ -359,44 +325,28 @@ static void testMaximizeSize() { EXPECT(str.contains("size: 1876,1036"), true); EXPECT(str.contains("fullscreen: 1"), true); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); } -static void testFloatingFocusOnFullscreen() { - NLog::log("{}Testing floating focus on fullscreen", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); +TEST_CASE(floatingFocusOnFullscreen) { + ASSERT(spawnKitty("kitty_A"), true); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'toggle' })")); - EXPECT(spawnKitty("kitty_B"), true); + ASSERT(spawnKitty("kitty_B"), true); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'maximized' })")); OK(getFromSocket("/dispatch hl.dsp.window.cycle_next()")); OK(getFromSocket("/eval hl.plugin.test.floating_focus_on_fullscreen()")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); } -static void testGroupFallbackFocus() { - NLog::log("{}Testing group fallback focus", Colors::GREEN); - - EXPECT(spawnKitty("kitty_A"), true); +TEST_CASE(groupFallbackFocus) { + ASSERT(spawnKitty("kitty_A"), true); OK(getFromSocket("/dispatch hl.dsp.group.toggle()")); - EXPECT(spawnKitty("kitty_B"), true); - EXPECT(spawnKitty("kitty_C"), true); - EXPECT(spawnKitty("kitty_D"), true); + ASSERT(spawnKitty("kitty_B"), true); + ASSERT(spawnKitty("kitty_C"), true); + ASSERT(spawnKitty("kitty_D"), true); { auto str = getFromSocket("/activewindow"); @@ -414,27 +364,18 @@ static void testGroupFallbackFocus() { auto str = getFromSocket("/activewindow"); EXPECT(str.contains("class: kitty_B"), true); } - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); } -static void testBringActiveToTopMouseMovement() { - NLog::log("{}Testing bringactivetotop mouse movement", Colors::GREEN); - - Tests::killAllWindows(); +TEST_CASE(bringActiveToTopMouseMovement) { OK(getFromSocket("/eval hl.config({ input = { follow_mouse = 2 } })")); OK(getFromSocket("/eval hl.config({ input = { float_switch_override_focus = 0 } })")); - EXPECT(spawnKitty("a"), true); + ASSERT(spawnKitty("a"), true); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 500, y = 300 })")); OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 400, y = 400 })")); - EXPECT(spawnKitty("b"), true); + ASSERT(spawnKitty("b"), true); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set' })")); OK(getFromSocket("/dispatch hl.dsp.window.move({ x = 500, y = 300 })")); OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 400, y = 400 })")); @@ -444,31 +385,26 @@ static void testBringActiveToTopMouseMovement() { return (clients.rfind("class: a") > clients.rfind("class: b")) ? "a" : "b"; }; - EXPECT(getTopWindow(), std::string("b")); + ASSERT(getTopWindow(), std::string("b")); OK(getFromSocket("/dispatch hl.dsp.cursor.move({ x = 700, y = 500 })")); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:a' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "class: a"); + ASSERT_CONTAINS(getFromSocket("/activewindow"), "class: a"); OK(getFromSocket("/dispatch hl.dsp.window.bring_to_top()")); - EXPECT(getTopWindow(), std::string("a")); + ASSERT(getTopWindow(), std::string("a")); OK(getFromSocket("/eval hl.plugin.test.click(272, 1)")); OK(getFromSocket("/eval hl.plugin.test.click(272, 0)")); - EXPECT(getTopWindow(), std::string("a")); - - Tests::killAllWindows(); + ASSERT(getTopWindow(), std::string("a")); } -static void testInitialFloatSize() { - NLog::log("{}Testing initial float size", Colors::GREEN); - - Tests::killAllWindows(); +TEST_CASE(initialFloatSize) { OK(getFromSocket("/eval hl.window_rule({ match = { class = 'kitty' }, float = true })")); OK(getFromSocket("/eval hl.config({ input = { float_switch_override_focus = 0 } })")); - EXPECT(spawnKitty("kitty"), true); + ASSERT(spawnKitty("kitty"), true); { // Kitty by default opens as 640x400, if this changes this test will break @@ -490,21 +426,17 @@ static void testInitialFloatSize() { EXPECT(str.contains("size: 640,400"), true); EXPECT(str.contains("floating: 1"), true); } - - Tests::killAllWindows(); } /// Tests that the `focus_on_activate` effect of window rules always overrides /// the `misc:focus_on_activate` variable. -static bool testWindowRuleFocusOnActivate() { - OK(getFromSocket("/reload")); - +TEST_CASE(windowRuleFocusOnActivate) { if (!spawnKitty("kitty_default")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } // Do not focus anyone automatically + // TODO: this looks like a bug: the following line should not be commented out ///////////OK(getFromSocket("/eval hl.window_rule({ match = { class = '.*' }, no_initial_focus = true })")); // `focus_on_activate off` takes over @@ -514,15 +446,15 @@ static bool testWindowRuleFocusOnActivate() { const std::string removeToActivate = spawnKittyActivating("kitty_antifocus"); if (removeToActivate.empty()) { - return false; + FAIL_TEST("Could not spawn kitty_antifocus"); } - EXPECT(waitForActiveWindow("kitty_antifocus"), true); + ASSERT(waitForActiveWindow("kitty_antifocus"), true); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_default' })")); - EXPECT(isActiveWindow("kitty_default"), true); + ASSERT(isActiveWindow("kitty_default"), true); std::filesystem::remove(removeToActivate); // The focus should NOT transition, since the window rule explicitly forbids that - EXPECT(waitForActiveWindow("kitty_antifocus", '0', false), false); + ASSERT(waitForActiveWindow("kitty_antifocus", '0', false), false); } // `focus_on_activate on` takes over @@ -532,37 +464,24 @@ static bool testWindowRuleFocusOnActivate() { const std::string removeToActivate = spawnKittyActivating("kitty_superfocus"); if (removeToActivate.empty()) { - return false; + FAIL_TEST("Could not spawn kitty_superfocus"); } - EXPECT(waitForActiveWindow("kitty_superfocus"), true); + ASSERT(waitForActiveWindow("kitty_superfocus"), true); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_default' })")); - EXPECT(isActiveWindow("kitty_default"), true); + ASSERT(isActiveWindow("kitty_default"), true); std::filesystem::remove(removeToActivate); // Now that we requested activation, the focus SHOULD transition to kitty_superfocus, according to the window rule - EXPECT(waitForActiveWindow("kitty_superfocus"), true); + ASSERT(waitForActiveWindow("kitty_superfocus"), true); } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; } // tests if a pinned window contains the valid workspace after change -static bool testPinnedWorkspacesValid() { - OK(getFromSocket("/reload")); +TEST_CASE(pinnedWorkspacesValid) { getFromSocket("/dispatch hl.dsp.focus({ workspace = '1337' })"); if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'set', window = 'class:kitty' })")); @@ -582,38 +501,23 @@ static bool testPinnedWorkspacesValid() { EXPECT(str.contains("pinned: 1"), true); } - OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'unset', window = 'class:kitty' })")) + OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'unset', window = 'class:kitty' })")); { auto str = getFromSocket("/activewindow"); EXPECT(str.contains("workspace: 1338"), true); EXPECT(str.contains("pinned: 0"), true); } - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return true; } -static bool testWindowRuleWorkspaceEmpty() { - NLog::log("{}Testing windowrule workspace empty", Colors::YELLOW); - OK(getFromSocket("/reload")); - +TEST_CASE(windowruleWorkspaceEmpty) { OK(getFromSocket("/eval hl.window_rule({ match = { class = 'kitty_A' }, workspace = 'empty' })")); OK(getFromSocket("/eval hl.window_rule({ match = { class = 'kitty_B' }, workspace = 'emptyn' })")); getFromSocket("/dispatch hl.dsp.focus({ workspace = '3' })"); if (!spawnKitty("kitty")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } { @@ -622,8 +526,7 @@ static bool testWindowRuleWorkspaceEmpty() { } if (!spawnKitty("kitty_A")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } { @@ -633,23 +536,16 @@ static bool testWindowRuleWorkspaceEmpty() { getFromSocket("/dispatch hl.dsp.focus({ workspace = '3' })"); if (!spawnKitty("kitty_B")) { - NLog::log("{}Error: failed to spawn kitty", Colors::RED); - return false; + FAIL_TEST("Could not spawn kitty"); } { auto str = getFromSocket("/activewindow"); EXPECT(str.contains("workspace: 4"), true); } - - Tests::killAllWindows(); - - return true; } -static bool testContentRules() { - NLog::log("{}Testing content window rules", Colors::YELLOW); - +TEST_CASE(contentWindowRules) { // kill me PLEASE OK(getFromSocket("/eval hl.window_rule({ match = { class = 'kitty_content_string' }, content = 'game' })")); @@ -657,33 +553,32 @@ static bool testContentRules() { OK(getFromSocket("/eval hl.window_rule({ match = { content = 'game' }, border_size = 10 })")); OK(getFromSocket("/eval hl.window_rule({ match = { content = '3' }, opacity = '0.5' })")); - const auto testProps = []() { - EXPECT_CONTAINS(getFromSocket("/getprop active border_size"), "10"); - EXPECT_CONTAINS(getFromSocket("/getprop active opacity"), "0.5"); - }; +#define TEST_PROPS() \ + EXPECT_CONTAINS(getFromSocket("/getprop active border_size"), "10"); \ + EXPECT_CONTAINS(getFromSocket("/getprop active opacity"), "0.5"); + if (!spawnKitty("kitty_content_string")) - return false; + FAIL_TEST("Could not spawn kitty_content_string"); waitForActiveWindow("kitty_content_string"); - testProps(); + TEST_PROPS(); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); if (!spawnKitty("kitty_content_numbers")) - return false; + FAIL_TEST("Could not spawn kitty_content_numbers"); waitForActiveWindow("kitty_content_numbers"); - testProps(); + TEST_PROPS(); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return true; -} + ASSERT(Tests::windowCount(), 0); -static bool test14038() { - NLog::log("{}Testing #14038 crash", Colors::YELLOW); +#undef TEST_PROPS +} +TEST_CASE(issue14038) { if (!spawnKitty("kitty_14038")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.window.move({ workspace = 'special:a', follow = false, window = 'class:kitty_14038' })")); OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'toggle', window = 'class:kitty_14038' })")); @@ -691,21 +586,16 @@ static bool test14038() { OK(getFromSocket("/dispatch hl.dsp.window.float({ action = 'toggle', window = 'class:kitty_14038' })")); // this should not crash hyprland. If we are alive, we good. - - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); - return true; } -static bool test() { - NLog::log("{}Testing windows", Colors::GREEN); - +// TODO: decompose this into multiple test cases +TEST_CASE(windows) { // test on workspace "window" NLog::log("{}Switching to workspace `window`", Colors::YELLOW); getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:window' })"); if (!spawnKitty("kitty_A")) - return false; + FAIL_TEST("Could not spawn kitty"); // check kitty properties. One kitty should take the entire screen, as this is smart gaps NLog::log("{}Expecting kitty_A to take up the whole screen", Colors::YELLOW); @@ -749,7 +639,7 @@ static bool test() { OK(getFromSocket("/eval hl.config({ dwindle = { default_split_ratio = 1.25 } })")); if (!spawnKitty("kitty_B")) - return false; + FAIL_TEST("Could not spawn kitty"); NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH1, HEIGHT); EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT)); @@ -761,7 +651,7 @@ static bool test() { OK(getFromSocket("/eval hl.config({ dwindle = { default_split_ratio = 0.75 } })")); if (!spawnKitty("kitty_B")) - return false; + FAIL_TEST("Could not spawn kitty"); try { NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT); @@ -791,10 +681,7 @@ static bool test() { EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2); } - } catch (...) { - NLog::log("{}Exception thrown", Colors::RED); - EXPECT(false, true); - } + } catch (...) { FAIL_TEST("Exception thrown"); } OK(getFromSocket("/eval hl.config({ dwindle = { default_split_ratio = 1 } })")); } @@ -807,7 +694,7 @@ static bool test() { Tests::waitUntilWindowsN(3); NLog::log("{}Expecting 3 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 3); + ASSERT(Tests::windowCount(), 3); NLog::log("{}Checking props of xeyes", Colors::YELLOW); // check some window props of xeyes, try to float it @@ -825,23 +712,16 @@ static bool test() { Tests::killAllWindows(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - testSwapWindow(); + ASSERT(Tests::windowCount(), 0); getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); - if (!testWindowFocusOnFullscreenConflict()) { - ret = 1; - return false; - } - NLog::log("{}Testing spawning a floating window over a fullscreen window", Colors::YELLOW); { if (!spawnKitty("kitty_A")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen', action = 'set' })")); - EXPECT(Tests::windowCount(), 1); + ASSERT(Tests::windowCount(), 1); OK(getFromSocket("/dispatch hl.dsp.exec_cmd('kitty', { float = true })")); Tests::waitUntilWindowsN(2); @@ -869,7 +749,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'kitty-max-rule', max_size = '1500 500' })")); OK(getFromSocket("r/eval hl.window_rule({ name = 'kitty-max-rule', min_size = '1200 500' })")); if (!spawnKitty("kitty_maxsize")) - return false; + FAIL_TEST("Could not spawn kitty"); auto dwindle = getFromSocket("/activewindow"); EXPECT_CONTAINS(dwindle, "size: 1500,500"); @@ -881,19 +761,19 @@ static bool test() { // EXPECT_CONTAINS(getFromSocket("/activewindow"), "size: 1200,500"); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); OK(getFromSocket("r/eval hl.config({ general = { layout = 'master' } })")); if (!spawnKitty("kitty_maxsize")) - return false; + FAIL_TEST("Could not spawn kitty"); auto master = getFromSocket("/activewindow"); EXPECT_CONTAINS(master, "size: 1500,500"); EXPECT_CONTAINS(master, "at: 210,290"); if (!spawnKitty("kitty_maxsize")) - return false; + FAIL_TEST("Could not spawn kitty"); // FIXME: I can't be arsed. OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_maxsize' })")); @@ -902,7 +782,7 @@ static bool test() { NLog::log("{}Reloading config", Colors::YELLOW); OK(getFromSocket("/reload")); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } NLog::log("{}Testing minsize/maxsize rules", Colors::YELLOW); @@ -913,7 +793,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'kitty-max-rule', max_size = '1500 500' })")); OK(getFromSocket("r/eval hl.window_rule({ name = 'kitty-max-rule', min_size = '1200 500' })")); if (!spawnKitty("kitty_maxsize")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto res = getFromSocket("/getprop active max_size"); @@ -930,7 +810,7 @@ static bool test() { NLog::log("{}Reloading config", Colors::YELLOW); OK(getFromSocket("/reload")); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } { @@ -940,7 +820,7 @@ static bool test() { OK(getFromSocket("r/eval hl.window_rule({ name = 'kitty-max-rule', min_size = '1200 500' })")); OK(getFromSocket("r/eval hl.window_rule({ name = 'kitty-max-rule', float = true })")); if (!spawnKitty("kitty_maxsize")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto res = getFromSocket("/getprop active max_size"); @@ -962,12 +842,12 @@ static bool test() { NLog::log("{}Reloading config", Colors::YELLOW); OK(getFromSocket("/reload")); Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); } NLog::log("{}Testing window rules", Colors::YELLOW); if (!spawnKitty("wr_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); const int SIZE = 200; @@ -987,7 +867,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'special-magic-kitty', workspace = 'special:magic' })")); if (!spawnKitty("magic_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); @@ -1005,7 +885,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'border-magic-kitty', border_color = 'rgba(c6ff00ff) rgba(ff0000ee) 45deg' })")); if (!spawnKitty("border_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:border_kitty' })")); @@ -1019,7 +899,7 @@ static bool test() { Tests::killAllWindows(); if (!spawnKitty("tag_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); @@ -1034,7 +914,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ match = { fullscreen = false }, border_size = 10 })")); if (!spawnKitty("overlap_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/getprop active border_size"); @@ -1048,9 +928,9 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ match = { class = 'persistent_size_kitty' }, persistent_size = true, float = true })")); if (!spawnKitty("persistent_size_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); - OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 600, y = 400 })")) + OK(getFromSocket("/dispatch hl.dsp.window.resize({ x = 600, y = 400 })")); { auto str = getFromSocket("/activewindow"); @@ -1061,7 +941,7 @@ static bool test() { Tests::killAllWindows(); if (!spawnKitty("persistent_size_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); @@ -1076,7 +956,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ match = { float = true }, border_size = 10 })")); if (!spawnKitty("border_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/getprop active border_size"); @@ -1105,7 +985,7 @@ static bool test() { "max_size = 'monitor_w*0.75 monitor_h*0.75', move = '20+(monitor_w*0.1) monitor_h*0.5' })")); if (!spawnKitty("expr_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); @@ -1131,7 +1011,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ match = { class = 'plugin_kitty' }, plugin_rule = 'effect' })")); if (!spawnKitty("plugin_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/eval hl.plugin.test.check_window_rule()")); @@ -1145,35 +1025,7 @@ static bool test() { OK(getFromSocket("/eval hl.window_rule({ name = 'test-plugin-rule', plugin_rule = 'effect' })")); if (!spawnKitty("plugin_kitty")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/eval hl.plugin.test.check_window_rule()")); - - OK(getFromSocket("/reload")); - Tests::killAllWindows(); - - testGroupRules(); - testMaximizeSize(); - testFloatingFocusOnFullscreen(); - testBringActiveToTopMouseMovement(); - testGroupFallbackFocus(); - testInitialFloatSize(); - testWindowRuleFocusOnActivate(); - testPinnedWorkspacesValid(); - testWindowRuleWorkspaceEmpty(); - testContentRules(); - test14038(); - - NLog::log("{}Reloading config", Colors::YELLOW); - OK(getFromSocket("/reload")); - - NLog::log("{}Killing all windows", Colors::YELLOW); - Tests::killAllWindows(); - - NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp index 2975b568..8cde4914 100644 --- a/hyprtester/src/tests/main/workspaces.cpp +++ b/hyprtester/src/tests/main/workspaces.cpp @@ -11,8 +11,6 @@ #include <cerrno> #include "../shared.hpp" -static int ret = 0; - using namespace Hyprutils::OS; using namespace Hyprutils::Memory; using namespace Hyprutils::Utils; @@ -20,7 +18,11 @@ using namespace Hyprutils::Utils; #define UP CUniquePointer #define SP CSharedPointer -static bool testSpecialWorkspaceFullscreen() { +// All the `SUBTEST`s below are supposed to be independent `TEST_CASE`s. +// But if isolated trivially, some of them fail. +// TODO: investigate and isolate tests by turning `SUBTEST`s into `TEST_CASE`s. + +SUBTEST(specialWorkspaceFullscreen) { NLog::log("{}Testing special workspace fullscreen detection", Colors::YELLOW); CScopeGuard guard = {[&]() { @@ -34,14 +36,14 @@ static bool testSpecialWorkspaceFullscreen() { }}; getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); - EXPECT(Tests::windowCount(), 0); + ASSERT(Tests::windowCount(), 0); NLog::log("{}Test 1: Fullscreen detection on special workspace", Colors::YELLOW); OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'special:test_fs_special' })")); if (!Tests::spawnKitty("kitty_special")) - return false; + FAIL_TEST("Could not spawn kitty"); { auto str = getFromSocket("/activewindow"); @@ -68,7 +70,7 @@ static bool testSpecialWorkspaceFullscreen() { getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); if (!Tests::spawnKitty("kitty_regular")) - return false; + FAIL_TEST("Could not spawn kitty"); OK(getFromSocket("/dispatch hl.dsp.window.fullscreen({ mode = 'fullscreen' })")); @@ -101,99 +103,91 @@ static bool testSpecialWorkspaceFullscreen() { auto str = getFromSocket("/monitors"); EXPECT_CONTAINS(str, "special workspace: 0 ()"); } - - return true; } -static bool testAsymmetricGaps() { +SUBTEST(asymmetricGaps) { NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW); - { - CScopeGuard guard = {[&]() { - NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW); - Tests::killAllWindows(); - OK(getFromSocket("/reload")); - }}; + ASSERT(Tests::windowCount(), 0); + getFromSocket("/dispatch workspace 1"); + getFromSocket("/reload"); - OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:gap_split_test' })")); - OK(getFromSocket("r/eval hl.config({ general = { gaps_in = 0 } })")); - OK(getFromSocket("r/eval hl.config({ general = { border_size = 0 } })")); - OK(getFromSocket("r/eval hl.config({ dwindle = { split_width_multiplier = 1.0 } })")); - OK(getFromSocket("r/eval hl.workspace_rule({ workspace = 'name:gap_split_test', gaps_out = { top = 0, right = 1000, bottom = 0, left = 0 } })")); + OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'name:gap_split_test' })")); + OK(getFromSocket("r/eval hl.config({ general = { gaps_in = 0 } })")); + OK(getFromSocket("r/eval hl.config({ general = { border_size = 0 } })")); + OK(getFromSocket("r/eval hl.config({ dwindle = { split_width_multiplier = 1.0 } })")); + OK(getFromSocket("r/eval hl.workspace_rule({ workspace = 'name:gap_split_test', gaps_out = { top = 0, right = 1000, bottom = 0, left = 0 } })")); - NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW); - OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 0 } })")); + NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW); + OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 0 } })")); - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + FAIL_TEST("Could not spawn kitty"); - NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); - NLog::log("{}Testing force_split = 1", Colors::YELLOW); - OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 1 } })")); + NLog::log("{}Testing force_split = 1", Colors::YELLOW); + OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 1 } })")); - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + FAIL_TEST("Could not spawn kitty"); - NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); + NLog::log("{}Expecting horizontal split (C left of B)", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); - if (!Tests::spawnKitty("gaps_kitty_C")) - return false; + if (!Tests::spawnKitty("gaps_kitty_C")) + FAIL_TEST("Could not spawn kitty"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_C' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_C' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); - Tests::killAllWindows(); - EXPECT(Tests::windowCount(), 0); + Tests::killAllWindows(); + ASSERT(Tests::windowCount(), 0); - NLog::log("{}Testing force_split = 2", Colors::YELLOW); - OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 2 } })")); + NLog::log("{}Testing force_split = 2", Colors::YELLOW); + OK(getFromSocket("r/eval hl.config({ dwindle = { force_split = 2 } })")); - if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) - return false; + if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B")) + FAIL_TEST("Could not spawn kitty"); - NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); + NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_B' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540"); - NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); + NLog::log("{}Expecting horizontal split (C right of A)", Colors::YELLOW); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); - if (!Tests::spawnKitty("gaps_kitty_C")) - return false; + if (!Tests::spawnKitty("gaps_kitty_C")) + FAIL_TEST("Could not spawn kitty"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); - OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_C' })")); - EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); - } + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_A' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0"); + OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:gaps_kitty_C' })")); + EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0"); // kill all NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - - return true; } -static void testWorkspaceHistoryMultiMon() { +SUBTEST(workspaceHistoryMultiMon) { NLog::log("{}Testing multimon workspace history tracker", Colors::YELLOW); // Initial state: @@ -222,7 +216,7 @@ static void testWorkspaceHistoryMultiMon() { Tests::killAllWindows(); } -static void testMultimonBAF() { +SUBTEST(multimonBAF) { NLog::log("{}Testing multimon back and forth", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ binds = { workspace_back_and_forth = 1 } })")); @@ -284,7 +278,7 @@ static void testMultimonBAF() { Tests::killAllWindows(); } -static void testMultimonFocus() { +SUBTEST(multimonFocus) { NLog::log("{}Testing multimon focus and move", Colors::YELLOW); OK(getFromSocket("/eval hl.config({ input = { follow_mouse = 0 } })")); @@ -295,10 +289,7 @@ static void testMultimonFocus() { for (auto const& win : {"a", "b"}) { if (!Tests::spawnKitty(win)) { - NLog::log("{}Failed to spawn kitty with win class `{}`", Colors::RED, win); - ++TESTS_FAILED; - ret = 1; - return; + FAIL_TEST("Could not spawn kitty with win class `{}`", win); } } @@ -350,24 +341,24 @@ static void testMultimonFocus() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } { auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); + ASSERT_CONTAINS(str, "workspace ID 8 "); } OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'right' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: c"); + ASSERT_CONTAINS(str, "class: c"); } { auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); + ASSERT_CONTAINS(str, "workspace ID 8 "); } OK(getFromSocket("/dispatch hl.dsp.focus({ direction = 'left' })")); @@ -375,28 +366,28 @@ static void testMultimonFocus() { OK(getFromSocket("/eval hl.config({ general = { no_focus_fallback = true } })")); OK(getFromSocket("/eval hl.config({ binds = { window_direction_monitor_fallback = false } })")); - EXPECT_NOT(getFromSocket("/dispatch hl.dsp.focus({ direction = 'left' })"), "ok"); + ASSERT_NOT(getFromSocket("/dispatch hl.dsp.focus({ direction = 'left' })"), "ok"); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } { auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); + ASSERT_CONTAINS(str, "workspace ID 8 "); } OK(getFromSocket("/dispatch hl.dsp.window.move({ direction = 'left' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: b"); + ASSERT_CONTAINS(str, "class: b"); } { auto str = getFromSocket("/activeworkspace"); - EXPECT_CONTAINS(str, "workspace ID 8 "); + ASSERT_CONTAINS(str, "workspace ID 8 "); } OK(getFromSocket("/reload")); @@ -404,7 +395,7 @@ static void testMultimonFocus() { Tests::killAllWindows(); } -static void testDynamicWsEffects() { +SUBTEST(dynamicWsEffects) { // test dynamic workspace effects, they shouldn't lag OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '69' })")); @@ -414,29 +405,22 @@ static void testDynamicWsEffects() { OK(getFromSocket("r/eval hl.workspace_rule({ workspace = '69', border_size = 20 })")); OK(getFromSocket("r/eval hl.workspace_rule({ workspace = '69', no_rounding = true })")); - EXPECT(getFromSocket("/getprop class:bitch border_size"), "20"); - EXPECT(getFromSocket("/getprop class:bitch rounding"), "0"); + ASSERT(getFromSocket("/getprop class:bitch border_size"), "20"); + ASSERT(getFromSocket("/getprop class:bitch rounding"), "0"); OK(getFromSocket("/reload")); Tests::killAllWindows(); } -static bool test() { - NLog::log("{}Testing workspaces", Colors::GREEN); - - EXPECT(Tests::windowCount(), 0); - - // test on workspace "window" - NLog::log("{}Switching to workspace 1", Colors::YELLOW); - getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })"); - +// TODO: decompose this into multiple test cases +TEST_CASE(workspacesCombined) { NLog::log("{}Checking persistent no-mon", Colors::YELLOW); OK(getFromSocket("r/eval hl.workspace_rule({ workspace = '966', persistent = true })")); { auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "workspace ID 966 (966)"); + ASSERT_CONTAINS(str, "workspace ID 966 (966)"); } OK(getFromSocket("/reload")); @@ -444,10 +428,8 @@ static bool test() { NLog::log("{}Spawning kittyProc on ws 1", Colors::YELLOW); auto kittyProcA = Tests::spawnKitty(); - if (!kittyProcA) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } + if (!kittyProcA) + FAIL_TEST("Could not spawn kitty"); NLog::log("{}Switching to workspace 3", Colors::YELLOW); OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '3' })")); @@ -455,10 +437,8 @@ static bool test() { NLog::log("{}Spawning kittyProc on ws 3", Colors::YELLOW); auto kittyProcB = Tests::spawnKitty(); - if (!kittyProcB) { - NLog::log("{}Error: kitty did not spawn", Colors::RED); - return false; - } + if (!kittyProcB) + FAIL_TEST("Could not spawn kitty"); NLog::log("{}Switching to workspace 1", Colors::YELLOW); OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); @@ -468,14 +448,14 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 2 (2)"); + ASSERT_STARTS_WITH(str, "workspace ID 2 (2)"); } // check if the other workspaces are alive { auto str = getFromSocket("/workspaces"); - EXPECT_CONTAINS(str, "workspace ID 3 (3)"); - EXPECT_CONTAINS(str, "workspace ID 1 (1)"); + ASSERT_CONTAINS(str, "workspace ID 3 (3)"); + ASSERT_CONTAINS(str, "workspace ID 1 (1)"); } NLog::log("{}Switching to workspace 1", Colors::YELLOW); @@ -483,7 +463,7 @@ static bool test() { { auto str = getFromSocket("/workspaces"); - EXPECT_NOT_CONTAINS(str, "workspace ID 2 (2)"); + ASSERT_NOT_CONTAINS(str, "workspace ID 2 (2)"); } NLog::log("{}Switching to workspace m+1", Colors::YELLOW); @@ -491,7 +471,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } NLog::log("{}Switching to workspace -1", Colors::YELLOW); @@ -499,7 +479,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 2 (2)"); + ASSERT_STARTS_WITH(str, "workspace ID 2 (2)"); } NLog::log("{}Switching to workspace 1", Colors::YELLOW); @@ -510,7 +490,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 2 (2)"); + ASSERT_STARTS_WITH(str, "workspace ID 2 (2)"); } NLog::log("{}Switching to workspace r+1", Colors::YELLOW); @@ -518,7 +498,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } NLog::log("{}Switching to workspace r~1", Colors::YELLOW); @@ -526,7 +506,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } NLog::log("{}Switching to workspace empty", Colors::YELLOW); @@ -534,7 +514,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 2 (2)"); + ASSERT_STARTS_WITH(str, "workspace ID 2 (2)"); } NLog::log("{}Switching to workspace previous", Colors::YELLOW); @@ -542,7 +522,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } NLog::log("{}Switching to workspace name:TEST_WORKSPACE_NULL", Colors::YELLOW); @@ -550,7 +530,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID -1337 (TEST_WORKSPACE_NULL)"); + ASSERT_STARTS_WITH(str, "workspace ID -1337 (TEST_WORKSPACE_NULL)"); } NLog::log("{}Switching to workspace 1", Colors::YELLOW); @@ -558,14 +538,14 @@ static bool test() { // add a new monitor NLog::log("{}Adding a new monitor", Colors::YELLOW); - EXPECT(getFromSocket("/output create headless"), "ok") + ASSERT(getFromSocket("/output create headless"), "ok"); // should take workspace 2 { auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "active workspace: 2 (2)"); - EXPECT_CONTAINS(str, "active workspace: 1 (1)"); - EXPECT_CONTAINS(str, "HEADLESS-3"); + ASSERT_CONTAINS(str, "active workspace: 2 (2)"); + ASSERT_CONTAINS(str, "active workspace: 1 (1)"); + ASSERT_CONTAINS(str, "HEADLESS-3"); } // focus the first monitor @@ -573,7 +553,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } NLog::log("{}Switching to workspace r+1", Colors::YELLOW); @@ -581,7 +561,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } NLog::log("{}Switching to workspace r~2", Colors::YELLOW); @@ -590,7 +570,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } NLog::log("{}Switching to workspace m+1", Colors::YELLOW); @@ -598,7 +578,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } NLog::log("{}Switching to workspace 1", Colors::YELLOW); @@ -607,7 +587,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } NLog::log("{}Testing back_and_forth", Colors::YELLOW); @@ -616,7 +596,7 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } OK(getFromSocket("/eval hl.config({ binds = { workspace_back_and_forth = false } })")); @@ -627,8 +607,8 @@ static bool test() { { auto str = getFromSocket("/monitors"); - EXPECT_CONTAINS(str, "special workspace: -"); - EXPECT_CONTAINS(str, "special:HELLO"); + ASSERT_CONTAINS(str, "special workspace: -"); + ASSERT_CONTAINS(str, "special:HELLO"); } // no OK: will err (it shouldn't prolly but oh well) @@ -636,7 +616,7 @@ static bool test() { { auto str = getFromSocket("/monitors"); - EXPECT_COUNT_STRING(str, "special workspace: 0 ()", 2); + ASSERT_COUNT_STRING(str, "special workspace: 0 ()", 2); } OK(getFromSocket("/eval hl.config({ binds = { hide_special_on_workspace_change = false } })")); @@ -652,21 +632,21 @@ static bool test() { { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'previous' })")); { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 1 (1)"); + ASSERT_STARTS_WITH(str, "workspace ID 1 (1)"); } OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = 'previous' })")); { auto str = getFromSocket("/activeworkspace"); - EXPECT_STARTS_WITH(str, "workspace ID 3 (3)"); + ASSERT_STARTS_WITH(str, "workspace ID 3 (3)"); } OK(getFromSocket("/eval hl.config({ binds = { allow_workspace_cycles = false } })")); @@ -700,7 +680,7 @@ static bool test() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_C"); + ASSERT_CONTAINS(str, "class: kitty_C"); } OK(getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })")); @@ -711,7 +691,7 @@ static bool test() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_B"); + ASSERT_CONTAINS(str, "class: kitty_B"); } NLog::log("{}Testing movefocus_cycles_fullscreen", Colors::YELLOW); @@ -720,14 +700,14 @@ static bool test() { Tests::spawnKitty("kitty_D"); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_D"); + ASSERT_CONTAINS(str, "class: kitty_D"); } OK(getFromSocket("/dispatch hl.dsp.focus({ monitor = 'l' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_A"); + ASSERT_CONTAINS(str, "class: kitty_A"); } OK(getFromSocket("/eval hl.config({ binds = { movefocus_cycles_fullscreen = false } })")); @@ -737,14 +717,14 @@ static bool test() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_D"); + ASSERT_CONTAINS(str, "class: kitty_D"); } OK(getFromSocket("/dispatch hl.dsp.focus({ monitor = 'l' })")); { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_A"); + ASSERT_CONTAINS(str, "class: kitty_A"); } OK(getFromSocket("/eval hl.config({ binds = { movefocus_cycles_fullscreen = true } })")); @@ -753,28 +733,24 @@ static bool test() { { auto str = getFromSocket("/activewindow"); - EXPECT_CONTAINS(str, "class: kitty_B"); + ASSERT_CONTAINS(str, "class: kitty_B"); } // kill all NLog::log("{}Killing all windows", Colors::YELLOW); Tests::killAllWindows(); - testMultimonBAF(); - testMultimonFocus(); - testWorkspaceHistoryMultiMon(); + CALL_SUBTEST(multimonBAF); + CALL_SUBTEST(multimonFocus); + CALL_SUBTEST(workspaceHistoryMultiMon); // destroy the headless output OK(getFromSocket("/output remove HEADLESS-3")); - testSpecialWorkspaceFullscreen(); - testAsymmetricGaps(); - testDynamicWsEffects(); + CALL_SUBTEST(specialWorkspaceFullscreen); + CALL_SUBTEST(asymmetricGaps); + CALL_SUBTEST(dynamicWsEffects); NLog::log("{}Expecting 0 windows", Colors::YELLOW); - EXPECT(Tests::windowCount(), 0); - - return !ret; + ASSERT(Tests::windowCount(), 0); } - -REGISTER_TEST_FN(test) diff --git a/hyprtester/src/tests/shared.cpp b/hyprtester/src/tests/shared.cpp index fd158a3e..03a64751 100644 --- a/hyprtester/src/tests/shared.cpp +++ b/hyprtester/src/tests/shared.cpp @@ -10,6 +10,10 @@ using namespace Hyprutils::OS; using namespace Hyprutils::Memory; +// Almost everywhere `Tests::spawnKitty` is used, the return value is immediately tested against `nullptr` +// and the test fails if it is. +// TODO: add a test macro for that. + CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std::vector<std::string> args) { const auto COUNT_BEFORE = windowCount(); |
