summaryrefslogtreecommitdiff
path: root/hyprtester
diff options
context:
space:
mode:
authorVaxry <43317083+vaxerski@users.noreply.github.com>2025-12-05 14:16:22 +0000
committerGitHub <noreply@github.com>2025-12-05 14:16:22 +0000
commit9264436f35e2c4e94adfdf2a8ea23a346ace3a65 (patch)
tree9fe48ff4728060445e655d8a3b528999fd124ff8 /hyprtester
parentd5c52ef58e57b371a9df8fd4404c5d55a1b26f34 (diff)
desktop: rewrite reserved area handling + improve tests (#12383)
Diffstat (limited to 'hyprtester')
-rw-r--r--hyprtester/plugin/src/main.cpp8
-rw-r--r--hyprtester/src/shared.hpp11
-rw-r--r--hyprtester/src/tests/main/keybinds.cpp46
-rw-r--r--hyprtester/src/tests/main/window.cpp75
-rw-r--r--hyprtester/src/tests/main/workspaces.cpp92
5 files changed, 192 insertions, 40 deletions
diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp
index cbc06723..b8706f52 100644
--- a/hyprtester/plugin/src/main.cpp
+++ b/hyprtester/plugin/src/main.cpp
@@ -225,7 +225,7 @@ static SDispatchResult scroll(std::string in) {
}
static SDispatchResult keybind(std::string in) {
- CVarList data(in);
+ CVarList2 data(std::move(in));
// 0 = release, 1 = press
bool press;
// See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks
@@ -234,9 +234,9 @@ static SDispatchResult keybind(std::string in) {
// keycode
uint32_t key;
try {
- press = std::stoul(data[0]) == 1;
- modifier = std::stoul(data[1]);
- key = std::stoul(data[2]) - 8; // xkb offset
+ press = std::stoul(std::string{data[0]}) == 1;
+ modifier = std::stoul(std::string{data[1]});
+ key = std::stoul(std::string{data[2]}) - 8; // xkb offset
} catch (...) { return {.success = false, .error = "invalid input"}; }
uint32_t modifierMask = 0;
diff --git a/hyprtester/src/shared.hpp b/hyprtester/src/shared.hpp
index 43944c3c..1090aa9a 100644
--- a/hyprtester/src/shared.hpp
+++ b/hyprtester/src/shared.hpp
@@ -18,6 +18,17 @@ namespace Colors {
constexpr const char* RESET = "\x1b[0m";
};
+#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++; \
+ } else { \
+ NLog::log("{}Passed: {}{}. Got {}", Colors::GREEN, Colors::RESET, #expr, (RESULT - (desired))); \
+ TESTS_PASSED++; \
+ }
+
#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__); \
diff --git a/hyprtester/src/tests/main/keybinds.cpp b/hyprtester/src/tests/main/keybinds.cpp
index a2fe2f37..23f17abf 100644
--- a/hyprtester/src/tests/main/keybinds.cpp
+++ b/hyprtester/src/tests/main/keybinds.cpp
@@ -86,8 +86,7 @@ static void testBind() {
// press keybind
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
// await flag
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- EXPECT(checkFlag(), true);
+ EXPECT(attemptCheckFlag(20, 50), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
@@ -99,8 +98,7 @@ static void testBindKey() {
// press keybind
OK(getFromSocket("/dispatch plugin:test:keybind 1,0,29"));
// await flag
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
- EXPECT(checkFlag(), true);
+ EXPECT(attemptCheckFlag(20, 50), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
EXPECT(getFromSocket("/keyword unbind ,Y"), "ok");
@@ -116,7 +114,7 @@ static void testLongPress() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT(checkFlag(), false);
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
@@ -133,7 +131,7 @@ static void testKeyLongPress() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT(checkFlag(), false);
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
@@ -152,7 +150,7 @@ static void testLongPressRelease() {
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
}
@@ -169,7 +167,7 @@ static void testLongPressOnlyKeyRelease() {
// release key, keep modifier
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
@@ -182,13 +180,13 @@ static void testRepeat() {
// press keybind
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
// await flag
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// check that it continues repeating
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
@@ -205,10 +203,10 @@ static void testKeyRepeat() {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
EXPECT(checkFlag(), true);
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// check that it continues repeating
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
@@ -227,10 +225,12 @@ static void testRepeatRelease() {
// release keybind
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ clearFlag();
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
// check that it is not repeating
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
}
@@ -242,15 +242,17 @@ static void testRepeatOnlyKeyRelease() {
// press keybind
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
// await flag
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), true);
// release key, keep modifier
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ clearFlag();
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
// check that it is not repeating
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
EXPECT(checkFlag(), false);
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
EXPECT(getFromSocket("/keyword unbind SUPER,Y"), "ok");
@@ -315,9 +317,9 @@ static void testShortcutLongPress() {
// press keybind
OK(getFromSocket("/dispatch plugin:test:keybind 1,7,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(150));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
OK(getFromSocket("/dispatch plugin:test:keybind 0,0,29"));
- std::this_thread::sleep_for(std::chrono::milliseconds(150));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
const std::string output = readKittyOutput();
int yCount = Tests::countOccurrences(output, "y");
// sometimes 1, sometimes 2, not sure why
@@ -347,7 +349,7 @@ static void testShortcutLongPressKeyRelease() {
// release key, keep modifier
OK(getFromSocket("/dispatch plugin:test:keybind 0,7,29"));
// await repeat delay
- std::this_thread::sleep_for(std::chrono::milliseconds(150));
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
const std::string output = readKittyOutput();
// disabled: doesn't work on CI
// EXPECT_COUNT_STRING(output, "y", 1);
@@ -485,6 +487,8 @@ static void testSubmapUniversal() {
static bool test() {
NLog::log("{}Testing keybinds", Colors::GREEN);
+ clearFlag();
+
testBind();
testBindKey();
testLongPress();
diff --git a/hyprtester/src/tests/main/window.cpp b/hyprtester/src/tests/main/window.cpp
index 3265bf72..5fa8b58f 100644
--- a/hyprtester/src/tests/main/window.cpp
+++ b/hyprtester/src/tests/main/window.cpp
@@ -6,6 +6,7 @@
#include <thread>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/memory/WeakPtr.hpp>
+#include <hyprutils/string/VarList2.hpp>
#include "../../shared.hpp"
#include "../../hyprctlCompat.hpp"
@@ -364,22 +365,40 @@ static bool test() {
NLog::log("{}Testing window split ratios", Colors::YELLOW);
{
- const double RATIO = 1.25;
- const double PERCENT = RATIO / 2.0 * 100.0;
- const int GAPSIN = 5;
- const int GAPSOUT = 20;
- const int BORDERS = 2 * 2;
- const int WTRIM = BORDERS + GAPSIN + GAPSOUT;
- const int HEIGHT = 1080 - (BORDERS + (GAPSOUT * 2));
- const int WIDTH1 = std::round(1920.0 / 2.0 * (2 - RATIO)) - WTRIM;
- const int WIDTH2 = std::round(1920.0 / 2.0 * RATIO) - WTRIM;
+ const double INITIAL_RATIO = 1.25;
+ const int GAPSIN = 5;
+ const int GAPSOUT = 20;
+ const int BORDERSIZE = 2;
+ const int BORDERS = BORDERSIZE * 2;
+ const int MONITOR_W = 1920;
+ const int MONITOR_H = 1080;
+
+ const float totalAvailableHeight = MONITOR_H - (GAPSOUT * 2);
+ const int HEIGHT = std::floor(totalAvailableHeight) - BORDERS;
+ const float availableWidthForSplit = MONITOR_W - (GAPSOUT * 2) - GAPSIN;
+
+ auto calculateFinalWidth = [&](double boxWidth, bool isLeftWindow) {
+ double gapLeft = isLeftWindow ? GAPSOUT : GAPSIN;
+ double gapRight = isLeftWindow ? GAPSIN : GAPSOUT;
+ return std::floor(boxWidth - gapLeft - gapRight - BORDERS);
+ };
+
+ double geomBoxWidthA_R1 = (availableWidthForSplit * INITIAL_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
+ double geomBoxWidthB_R1 = MONITOR_W - geomBoxWidthA_R1;
+ const int WIDTH1 = calculateFinalWidth(geomBoxWidthB_R1, false);
+
+ const double INVERTED_RATIO = 0.75;
+ double geomBoxWidthA_R2 = (availableWidthForSplit * INVERTED_RATIO / 2.0) + GAPSOUT + (GAPSIN / 2.0);
+ double geomBoxWidthB_R2 = MONITOR_W - geomBoxWidthA_R2;
+ const int WIDTH2 = calculateFinalWidth(geomBoxWidthB_R2, false);
+ const int WIDTH_A_FINAL = calculateFinalWidth(geomBoxWidthA_R2, true);
OK(getFromSocket("/keyword dwindle:default_split_ratio 1.25"));
if (!spawnKitty("kitty_B"))
return false;
- NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, 100 - PERCENT);
+ NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH1, HEIGHT);
EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
OK(getFromSocket("/dispatch killwindow activewindow"));
@@ -391,12 +410,38 @@ static bool test() {
if (!spawnKitty("kitty_B"))
return false;
- NLog::log("{}Expecting kitty_B to take up roughly {}% of screen width", Colors::YELLOW, PERCENT);
- EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH2, HEIGHT));
+ try {
+ NLog::log("{}Expecting kitty_B size: {},{}", Colors::YELLOW, WIDTH2, HEIGHT);
- OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
- NLog::log("{}Expecting kitty_A to have the same width as the previous kitty_B", Colors::YELLOW);
- EXPECT_CONTAINS(getFromSocket("/activewindow"), std::format("size: {},{}", WIDTH1, HEIGHT));
+ {
+ auto data = getFromSocket("/activewindow");
+ data = data.substr(data.find("size:") + 5);
+ data = data.substr(0, data.find('\n'));
+
+ Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
+
+ EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH2, 2);
+ EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
+ }
+
+ OK(getFromSocket("/dispatch focuswindow class:kitty_A"));
+ NLog::log("{}Expecting kitty_A size: {},{}", Colors::YELLOW, WIDTH_A_FINAL, HEIGHT);
+
+ {
+ auto data = getFromSocket("/activewindow");
+ data = data.substr(data.find("size:") + 5);
+ data = data.substr(0, data.find('\n'));
+
+ Hyprutils::String::CVarList2 sizes(std::move(data), 0, ',');
+
+ EXPECT_MAX_DELTA(std::stoi(std::string{sizes[0]}), WIDTH_A_FINAL, 2);
+ EXPECT_MAX_DELTA(std::stoi(std::string{sizes[1]}), HEIGHT, 2);
+ }
+
+ } catch (...) {
+ NLog::log("{}Exception thrown", Colors::RED);
+ EXPECT(false, true);
+ }
OK(getFromSocket("/keyword dwindle:default_split_ratio 1"));
}
diff --git a/hyprtester/src/tests/main/workspaces.cpp b/hyprtester/src/tests/main/workspaces.cpp
index 622236dc..c1b9690a 100644
--- a/hyprtester/src/tests/main/workspaces.cpp
+++ b/hyprtester/src/tests/main/workspaces.cpp
@@ -6,6 +6,7 @@
#include <chrono>
#include <hyprutils/os/Process.hpp>
#include <hyprutils/memory/WeakPtr.hpp>
+#include <hyprutils/utils/ScopeGuard.hpp>
#include <csignal>
#include <cerrno>
#include "../shared.hpp"
@@ -14,10 +15,99 @@ static int ret = 0;
using namespace Hyprutils::OS;
using namespace Hyprutils::Memory;
+using namespace Hyprutils::Utils;
#define UP CUniquePointer
#define SP CSharedPointer
+static bool testAsymmetricGaps() {
+ NLog::log("{}Testing asymmetric gap splits", Colors::YELLOW);
+ {
+
+ CScopeGuard guard = {[&]() {
+ NLog::log("{}Cleaning up asymmetric gap test", Colors::YELLOW);
+ Tests::killAllWindows();
+ OK(getFromSocket("/reload"));
+ }};
+
+ OK(getFromSocket("/dispatch workspace name:gap_split_test"));
+ OK(getFromSocket("r/keyword general:gaps_in 0"));
+ OK(getFromSocket("r/keyword general:border_size 0"));
+ OK(getFromSocket("r/keyword dwindle:split_width_multiplier 1.0"));
+ OK(getFromSocket("r/keyword workspace name:gap_split_test,gapsout:0 1000 0 0"));
+
+ NLog::log("{}Testing default split (force_split = 0)", Colors::YELLOW);
+ OK(getFromSocket("r/keyword dwindle:force_split 0"));
+
+ if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
+ return false;
+
+ NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,540");
+
+ Tests::killAllWindows();
+ EXPECT(Tests::windowCount(), 0);
+
+ NLog::log("{}Testing force_split = 1", Colors::YELLOW);
+ OK(getFromSocket("r/keyword dwindle:force_split 1"));
+
+ if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
+ return false;
+
+ NLog::log("{}Expecting vertical split (B above A)", Colors::YELLOW);
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
+ OK(getFromSocket("/dispatch focuswindow 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 focuswindow class:gaps_kitty_B"));
+
+ if (!Tests::spawnKitty("gaps_kitty_C"))
+ return false;
+
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_C"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_B"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 460,0");
+
+ Tests::killAllWindows();
+ EXPECT(Tests::windowCount(), 0);
+
+ NLog::log("{}Testing force_split = 2", Colors::YELLOW);
+ OK(getFromSocket("r/keyword dwindle:force_split 2"));
+
+ if (!Tests::spawnKitty("gaps_kitty_A") || !Tests::spawnKitty("gaps_kitty_B"))
+ return false;
+
+ NLog::log("{}Expecting vertical split (B below A)", Colors::YELLOW);
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
+ OK(getFromSocket("/dispatch focuswindow 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 focuswindow class:gaps_kitty_A"));
+
+ if (!Tests::spawnKitty("gaps_kitty_C"))
+ return false;
+
+ OK(getFromSocket("/dispatch focuswindow class:gaps_kitty_A"));
+ EXPECT_CONTAINS(getFromSocket("/activewindow"), "at: 0,0");
+ OK(getFromSocket("/dispatch focuswindow 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 bool test() {
NLog::log("{}Testing workspaces", Colors::GREEN);
@@ -359,6 +449,8 @@ static bool test() {
NLog::log("{}Killing all windows", Colors::YELLOW);
Tests::killAllWindows();
+ testAsymmetricGaps();
+
NLog::log("{}Expecting 0 windows", Colors::YELLOW);
EXPECT(Tests::windowCount(), 0);