1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
#include "../../hyprctlCompat.hpp"
#include "../shared.hpp"
#include "tests.hpp"
#include "build.hpp"
#include <hyprutils/os/FileDescriptor.hpp>
#include <hyprutils/os/Process.hpp>
#include <optional>
#include <sys/poll.h>
#include <unistd.h>
#include <csignal>
#include <thread>
using namespace Hyprutils::OS;
using namespace Hyprutils::Memory;
#define SP CSharedPointer
static bool waitForWindow(SP<CProcess> proc, int windowsBefore) {
int counter = 0;
while (Tests::processAlive(proc->pid()) && Tests::windowCount() == windowsBefore) {
counter++;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (counter > 50)
return false;
}
NLog::log("{}Waited {} milliseconds for window to open", Colors::YELLOW, counter * 100);
return Tests::processAlive(proc->pid());
}
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);
this->proc = makeShared<CProcess>(binaryDir + "/child-window", std::vector<std::string>{});
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);
throw std::exception();
}
this->writeFd = CFileDescriptor(procInPipeFd[1]);
this->proc->setStdinFD(procInPipeFd[0]);
this->readFd = CFileDescriptor(procOutPipeFd[0]);
this->proc->setStdoutFD(procOutPipeFd[1]);
if (!this->proc->runAsync()) {
NLog::log("{}Failed to run client", Colors::RED);
throw std::exception();
}
close(procInPipeFd[0]);
close(procOutPipeFd[1]);
if (!waitForWindow(this->proc, Tests::windowCount())) {
NLog::log("{}Window took too long to open", Colors::RED);
throw std::exception();
}
NLog::log("{}Started child-window client", Colors::YELLOW);
}
CClient::~CClient() {
std::string cmd = "exit\n";
write(this->writeFd.get(), cmd.c_str(), cmd.length());
kill(this->proc->pid(), SIGKILL);
this->proc.reset();
}
bool CClient::createChild() {
std::string cmd = "toplevel\n";
if ((size_t)write(this->writeFd.get(), cmd.c_str(), cmd.length()) != cmd.length())
return false;
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") {
NLog::log("{}Failed to focus child window", Colors::RED);
return false;
}
return true;
}
TEST_CASE(childWindow) {
{
std::optional<CClient> client;
try {
client.emplace();
} catch (...) { FAIL_TEST("Couldn't start the client"); }
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' })"));
client->createChild();
EXPECT(Tests::windowCount(), 2);
EXPECT_COUNT_STRING(getFromSocket("/clients"), "pinned: 1", 2);
}
NLog::log("{}Reloading config", Colors::YELLOW);
OK(getFromSocket("/reload"));
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
// test that child windows (shouldBeFloated) are not auto-grouped
NLog::log("{}Test child windows are not auto-grouped", Colors::GREEN);
auto kitty = Tests::spawnKitty();
if (!kitty) {
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 } })"));
{
std::optional<CClient> client2;
try {
client2.emplace();
} catch (...) { FAIL_TEST("Couldn't start the client"); }
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);
}
Tests::killAllWindows();
EXPECT(Tests::windowCount(), 0);
}
|