#include "tests.hpp" #include "../../shared.hpp" #include "../../hyprctlCompat.hpp" #include #include #include #include #include #include #include #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); // test on workspace "window" NLog::log("{}Dispatching workspace `groups`", Colors::YELLOW); getFromSocket("/dispatch workspace name:groups"); NLog::log("{}Spawning kittyProcA", Colors::YELLOW); auto kittyProcA = Tests::spawnKitty(); if (!kittyProcA) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } NLog::log("{}Expecting 1 window", Colors::YELLOW); EXPECT(Tests::windowCount(), 1); // check kitty properties. One kitty should take the entire screen, minus the gaps. NLog::log("{}Check kitty dimensions", Colors::YELLOW); { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 1); EXPECT_COUNT_STRING(str, "size: 1876,1036", 1); EXPECT_COUNT_STRING(str, "fullscreen: 0", 1); } // group the kitty NLog::log("{}Enable group and groupbar", Colors::YELLOW); OK(getFromSocket("/dispatch togglegroup")); OK(getFromSocket("/keyword group:groupbar:enabled 1")); // check the height of the window now NLog::log("{}Recheck kitty dimensions", Colors::YELLOW); { auto str = getFromSocket("/clients"); EXPECT_CONTAINS(str, "at: 22,43"); EXPECT_CONTAINS(str, "size: 1876,1015"); } // disable the groupbar for ease of testing for now NLog::log("{}Disable groupbar", Colors::YELLOW); OK(getFromSocket("r/keyword group:groupbar:enabled 0")); // kill all NLog::log("{}Kill windows", Colors::YELLOW); Tests::killAllWindows(); NLog::log("{}Spawn kitty again", Colors::YELLOW); kittyProcA = Tests::spawnKitty(); if (!kittyProcA) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } NLog::log("{}Group kitty", Colors::YELLOW); OK(getFromSocket("/dispatch togglegroup")); // check the height of the window now NLog::log("{}Check kitty dimensions 2", Colors::YELLOW); { auto str = getFromSocket("/clients"); EXPECT_CONTAINS(str, "at: 22,22"); EXPECT_CONTAINS(str, "size: 1876,1036"); } NLog::log("{}Spawn kittyProcB", Colors::YELLOW); auto kittyProcB = Tests::spawnKitty(); if (!kittyProcB) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } NLog::log("{}Expecting 2 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 2); size_t lastActiveKittyIdx = 0; NLog::log("{}Get last active kitty id", Colors::YELLOW); 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; } // test cycling through NLog::log("{}Test cycling through grouped windows", Colors::YELLOW); OK(getFromSocket("/dispatch changegroupactive f")); 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; } getFromSocket("/dispatch changegroupactive f"); 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; } // test movegroupwindow: focus should follow the moved window NLog::log("{}Test movegroupwindow focus follows window", Colors::YELLOW); try { auto str = getFromSocket("/activewindow"); auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); OK(getFromSocket("/dispatch movegroupwindow f")); 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; } // and backwards NLog::log("{}Test movegroupwindow backwards", Colors::YELLOW); try { auto str = getFromSocket("/activewindow"); auto activeBeforeMove = std::stoull(str.substr(7, str.find(" -> ") - 7), nullptr, 16); OK(getFromSocket("/dispatch movegroupwindow b")); 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; } NLog::log("{}Disable autogrouping", Colors::YELLOW); OK(getFromSocket("/keyword group:auto_group false")); NLog::log("{}Spawn kittyProcC", Colors::YELLOW); auto kittyProcC = Tests::spawnKitty(); if (!kittyProcC) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } NLog::log("{}Expecting 3 windows 2", Colors::YELLOW); EXPECT(Tests::windowCount(), 3); { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 2); } OK(getFromSocket("/dispatch movefocus l")); OK(getFromSocket("/dispatch changegroupactive 1")); OK(getFromSocket("/keyword group:auto_group true")); OK(getFromSocket("/keyword group:insert_after_current false")); NLog::log("{}Spawn kittyProcD", Colors::YELLOW); auto kittyProcD = Tests::spawnKitty(); if (!kittyProcD) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } NLog::log("{}Expecting 4 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 4); OK(getFromSocket("/dispatch changegroupactive 3")); { auto str = getFromSocket("/activewindow"); EXPECT_CONTAINS(str, std::format("pid: {}", kittyProcD->pid())); } // kill all NLog::log("{}Kill windows", Colors::YELLOW); Tests::killAllWindows(); NLog::log("{}Expecting 0 windows", Colors::YELLOW); EXPECT(Tests::windowCount(), 0); // test movewindoworgroup: direction should be respected when extracting from group NLog::log("{}Test movewindoworgroup respects direction out of group", Colors::YELLOW); OK(getFromSocket("/keyword group:groupbar:enabled 0")); { auto kittyE = Tests::spawnKitty(); if (!kittyE) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } // group kitty, and new windows should be auto-grouped OK(getFromSocket("/dispatch togglegroup")); auto kittyF = Tests::spawnKitty(); if (!kittyF) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } EXPECT(Tests::windowCount(), 2); // both windows should be grouped at the same position { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 2); } // move active window out of group to the right NLog::log("{}Test movewindoworgroup r", Colors::YELLOW); OK(getFromSocket("/dispatch movewindoworgroup r")); // the group should stay at x=22, the extracted window should be to the right { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 1); } // move it back into the group OK(getFromSocket("/dispatch moveintogroup l")); // move active window out of group downward NLog::log("{}Test movewindoworgroup d", Colors::YELLOW); OK(getFromSocket("/dispatch movewindoworgroup d")); // the group should stay at y=22, the extracted window should be below { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 1); } Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); } // test that we deny a floated window getting auto-grouped into a tiled group. NLog::log("{}Test that we deny a floated window getting auto-grouped into a tiled group.", Colors::GREEN); OK(getFromSocket("/keyword windowrule[kitty-tiled]:match:class kitty_tiled")); OK(getFromSocket("/keyword windowrule[kitty-tiled]:tile yes")); auto kittyProcE = Tests::spawnKitty("kitty_tiled"); if (!kittyProcE) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } OK(getFromSocket("/dispatch togglegroup")); OK(getFromSocket("/keyword windowrule[kitty-floated]:match:class kitty_floated")); OK(getFromSocket("/keyword windowrule[kitty-floated]:float yes")); auto kittyProcF = Tests::spawnKitty("kitty_floated"); if (!kittyProcF) { NLog::log("{}Error: kitty did not spawn", Colors::RED); return false; } EXPECT(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; } else { auto entryStart = clients.rfind("Window ", classPos); auto entryEnd = clients.find("\n\n", classPos); auto windowEntry = clients.substr(entryStart, entryEnd - entryStart); EXPECT_CONTAINS(windowEntry, "floating: 1"); EXPECT_CONTAINS(windowEntry, "grouped: 0"); } } Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); // Tests for grouping/merging logic NLog::log("{}Testing locked groups w/ invade", Colors::GREEN); Tests::killAllWindows(); EXPECT(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; } OK(getFromSocket("/dispatch togglegroup")); auto winB = Tests::spawnKitty("top"); if (!winB) { NLog::log("{}Error: top kitty did not spawn", Colors::RED); return false; } // Verify it DID merge into a group { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 2); } } Tests::killAllWindows(); EXPECT(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; } OK(getFromSocket("/dispatch togglegroup")); OK(getFromSocket(std::format("/dispatch focuswindow pid:{}", lockedWin->pid()))); OK(getFromSocket("/dispatch lockactivegroup lock")); auto winB = Tests::spawnKitty("top"); if (!winB) { NLog::log("{}Error: top kitty did not spawn", Colors::RED); return false; } // Verify it did NOT merge into the locked group { auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 1); } } Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); // Test locked groups WITH invade rule { OK(getFromSocket("/keyword windowrule[locked-im]:match:class ^locked|invade$")); OK(getFromSocket("/keyword windowrule[locked-im]:group set always lock invade")); auto lockedWin = Tests::spawnKitty("locked"); if (!lockedWin) { NLog::log("{}Error: locked kitty did not spawn", Colors::RED); return false; } auto invadingWin = Tests::spawnKitty("invade"); if (!invadingWin) { NLog::log("{}Error: invading kitty did not spawn", Colors::RED); return false; } // Verify it DID merge into the locked group auto str = getFromSocket("/clients"); EXPECT_COUNT_STRING(str, "at: 22,22", 2); } Tests::killAllWindows(); EXPECT(Tests::windowCount(), 0); return !ret; } REGISTER_TEST_FN(test)