diff --git a/hyprtester/src/tests/main/colors.cpp b/hyprtester/src/tests/main/colors.cpp index 93dcbb651..e430145c5 100644 --- a/hyprtester/src/tests/main/colors.cpp +++ b/hyprtester/src/tests/main/colors.cpp @@ -8,7 +8,7 @@ TEST_CASE(monitorsColorManagement) { std::string monitorsSpec = getFromSocket("j/monitors"); - ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset")"); + ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset": )"); ASSERT_CONTAINS(getFromSocket("/eval hl.monitor({ output = 'HEADLESS-2', bitdepth = 10, cm = 'wide' })"), "ok"); @@ -16,14 +16,14 @@ TEST_CASE(monitorsColorManagement) { std::this_thread::sleep_for(std::chrono::milliseconds(500)); monitorsSpec = getFromSocket("j/monitors"); - ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset": "wide")"); + ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset": )"); 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)); - ASSERT_CONTAINS(monitorsSpec, "colorManagementPreset"); - ASSERT_CONTAINS(monitorsSpec, "sdrBrightness"); - ASSERT_CONTAINS(monitorsSpec, "sdrSaturation"); + ASSERT_CONTAINS(monitorsSpec, R"("colorManagementPreset": )"); + ASSERT_CONTAINS(monitorsSpec, R"("sdrBrightness": )"); + ASSERT_CONTAINS(monitorsSpec, R"("sdrSaturation": )"); } diff --git a/hyprtester/src/tests/main/dwindle.cpp b/hyprtester/src/tests/main/dwindle.cpp index 5641f8acf..e962efd1b 100644 --- a/hyprtester/src/tests/main/dwindle.cpp +++ b/hyprtester/src/tests/main/dwindle.cpp @@ -204,7 +204,7 @@ TEST_CASE(dwindleForceSplitOnMoveToWorkspace) { OK(getFromSocket("/dispatch hl.dsp.focus({ workspace = '1' })")); ASSERT(!!Tests::spawnKitty("kitty"), true); - std::string posBefore = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + std::string posBefore = "at: " + Tests::getAttribute(getFromSocket("/activewindow"), "at"); OK(getFromSocket("/eval hl.config({ dwindle = { force_split = 2 } })")); OK(getFromSocket("/dispatch hl.dsp.cursor.move_to_corner({ corner = 3 })")); // top left diff --git a/hyprtester/src/tests/main/follow_mouse_shrink.cpp b/hyprtester/src/tests/main/follow_mouse_shrink.cpp index 0e12915b0..600643c6b 100644 --- a/hyprtester/src/tests/main/follow_mouse_shrink.cpp +++ b/hyprtester/src/tests/main/follow_mouse_shrink.cpp @@ -9,9 +9,9 @@ static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) { std::string activeWin = getFromSocket("/activewindow"); - auto winClass = Tests::getWindowAttribute(activeWin, "class:"); - auto winFullscreen = Tests::getWindowAttribute(activeWin, "fullscreen:").back(); - if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen) + auto winClass = Tests::getAttribute(activeWin, "class"); + auto winFullscreen = Tests::getAttribute(activeWin, "fullscreen").back(); + if (winClass == class_ && winFullscreen == fullscreen) return true; else { if (log) diff --git a/hyprtester/src/tests/main/solitary.cpp b/hyprtester/src/tests/main/solitary.cpp index 3e124a1e9..522b7968f 100644 --- a/hyprtester/src/tests/main/solitary.cpp +++ b/hyprtester/src/tests/main/solitary.cpp @@ -1,8 +1,11 @@ #include "tests.hpp" #include "../../shared.hpp" #include "../../hyprctlCompat.hpp" -#include +#include #include +#include +#include +#include #include #include #include "../shared.hpp" @@ -13,6 +16,13 @@ using namespace Hyprutils::Memory; #define UP CUniquePointer #define SP CSharedPointer +SUBTEST(expectBlockedByAll, const std::string& blockedByLine, const std::set& expectedBlockedBy) { + const std::set blockedBy = blockedByLine | std::ranges::views::split(',') | std::ranges::to>(); + NLog::log("blockedBy = {}", blockedBy); + NLog::log("expectedBlockedBy = {}", expectedBlockedBy); + ASSERT(std::ranges::includes(blockedBy, expectedBlockedBy), true); +} + TEST_CASE(solitaryClients) { OK(getFromSocket("/eval hl.config({ general = { allow_tearing = false } })")); OK(getFromSocket("/eval hl.config({ render = { direct_scanout = 0 } })")); @@ -21,11 +31,12 @@ TEST_CASE(solitaryClients) { { auto str = getFromSocket("/monitors"); EXPECT_CONTAINS(str, "solitary: 0\n"); - EXPECT_CONTAINS(str, "solitaryBlockedBy: windowed mode,missing candidate"); + CALL_SUBTEST(expectBlockedByAll, Tests::getAttribute(str, "solitaryBlockedBy"), {"windowed mode", "missing candidate"}); EXPECT_CONTAINS(str, "activelyTearing: false"); - EXPECT_CONTAINS(str, "tearingBlockedBy: next frame is not torn,user settings,not supported by monitor,missing candidate"); + CALL_SUBTEST(expectBlockedByAll, Tests::getAttribute(str, "tearingBlockedBy"), + {"next frame is not torn", "user settings", "not supported by monitor", "missing candidate"}); EXPECT_CONTAINS(str, "directScanoutTo: 0\n"); - EXPECT_CONTAINS(str, "directScanoutBlockedBy: user settings,software renders/cursors,missing candidate"); + CALL_SUBTEST(expectBlockedByAll, Tests::getAttribute(str, "directScanoutBlockedBy"), {"user settings", "software renders/cursors", "missing candidate"}); } // FIXME: need a reliable client with solitary opaque surface in fullscreen. kitty doesn't work all the time diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp index 7f36cf7b7..4a4da0ccb 100644 --- a/hyprtester/src/tests/main/window.cpp +++ b/hyprtester/src/tests/main/window.cpp @@ -75,7 +75,7 @@ TEST_CASE(swapWindow) { // Test swapwindow by direction { getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })"); - auto pos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + auto pos = "at: " + Tests::getAttribute(getFromSocket("/activewindow"), "at"); NLog::log("{}Testing kitty_A {}, swapwindow with direction 'r'", Colors::YELLOW, pos); OK(getFromSocket("/dispatch hl.dsp.window.swap({ direction = 'right' })")); @@ -87,7 +87,7 @@ TEST_CASE(swapWindow) { // Test swapwindow by class { getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })"); - auto pos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + auto pos = "at: " + Tests::getAttribute(getFromSocket("/activewindow"), "at"); NLog::log("{}Testing kitty_A {}, swapwindow with class:kitty_B", Colors::YELLOW, pos); OK(getFromSocket("/dispatch hl.dsp.window.swap({ target = 'class:kitty_B' })")); @@ -101,7 +101,7 @@ TEST_CASE(swapWindow) { getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_B' })"); auto addr = getWindowAddress(getFromSocket("/activewindow")); getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_A' })"); - auto pos = Tests::getWindowAttribute(getFromSocket("/activewindow"), "at:"); + auto pos = "at: " + Tests::getAttribute(getFromSocket("/activewindow"), "at"); NLog::log("{}Testing kitty_A {}, swapwindow with address:0x{}(kitty_B)", Colors::YELLOW, pos, addr); OK(getFromSocket(std::format("/dispatch hl.dsp.window.swap({{ target = 'address:0x{}' }})", addr))); @@ -124,7 +124,7 @@ TEST_CASE(swapWindow) { { getFromSocket("/dispatch hl.dsp.focus({ window = 'class:kitty_B' })"); auto addr = getWindowAddress(getFromSocket("/activewindow")); - auto ws = Tests::getWindowAttribute(getFromSocket("/activewindow"), "workspace:"); + auto ws = "workspace: " + Tests::getAttribute(getFromSocket("/activewindow"), "workspace"); NLog::log("{}Sending address:0x{}(kitty_B) to workspace \"swapwindow2\"", Colors::YELLOW, addr); OK(getFromSocket("/dispatch hl.dsp.window.move({ workspace = 'name:swapwindow2', follow = false })")); @@ -190,9 +190,9 @@ TEST_CASE(windowGroupRules) { static bool isActiveWindow(const std::string& class_, char fullscreen = '0', bool log = true) { std::string activeWin = getFromSocket("/activewindow"); - auto winClass = Tests::getWindowAttribute(activeWin, "class:"); - auto winFullscreen = Tests::getWindowAttribute(activeWin, "fullscreen:").back(); - if (winClass.substr(strlen("class: ")) == class_ && winFullscreen == fullscreen) + auto winClass = Tests::getAttribute(activeWin, "class"); + auto winFullscreen = Tests::getAttribute(activeWin, "fullscreen").back(); + if (winClass == class_ && winFullscreen == fullscreen) return true; else { if (log) diff --git a/hyprtester/src/tests/shared.cpp b/hyprtester/src/tests/shared.cpp index 03a647519..d08f4ea23 100644 --- a/hyprtester/src/tests/shared.cpp +++ b/hyprtester/src/tests/shared.cpp @@ -1,4 +1,5 @@ #include "shared.hpp" +#include #include #include #include @@ -186,12 +187,14 @@ bool Tests::writeFile(const std::string& name, const std::string& contents) { return true; } -std::string Tests::getWindowAttribute(const std::string& winInfo, const std::string& attr) { - auto pos = winInfo.find(attr); +std::string Tests::getAttribute(const std::string& hyprlandResponse, std::string attr) { + attr += ": "; + auto pos = hyprlandResponse.find(attr); if (pos == std::string::npos) { NLog::log("{}Window attribute not found: '{}'", Colors::RED, attr); return "Wrong window attribute"; } - auto pos2 = winInfo.find('\n', pos); - return winInfo.substr(pos, pos2 - pos); + pos += attr.size(); + auto pos2 = hyprlandResponse.find('\n', pos); + return hyprlandResponse.substr(pos, pos2 - pos); } diff --git a/hyprtester/src/tests/shared.hpp b/hyprtester/src/tests/shared.hpp index 95058e040..fbef8a0e1 100644 --- a/hyprtester/src/tests/shared.hpp +++ b/hyprtester/src/tests/shared.hpp @@ -19,5 +19,11 @@ namespace Tests { bool killAllLayers(); std::string execAndGet(const std::string& cmd); bool writeFile(const std::string& name, const std::string& contents); - std::string getWindowAttribute(const std::string& winInfo, const std::string& attr); + /** + * Extracts the given attribute from Hyprland's response to requests such as `/clients`, `/workspaces`, etc. + * Automatically appends `: ` to `attr`. + * + * For example, `Tests::getAttribute(getFromSocket("/activewindow"), "at")` returns the active window's coordinates, e.g., `"2,32"` + */ + std::string getAttribute(const std::string& hyprlandResponse, std::string attr); };