1 /*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 // Qt
7 #include <QtTest>
8 // WaylandServer
9 #include "../../src/server/clientconnection.h"
10 #include "../../src/server/display.h"
11 #include "../../src/server/output_interface.h"
12 #include "../../src/server/outputmanagement_v2_interface.h"
13 // Wayland
14 #include <wayland-server.h>
15 // system
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19
20 using namespace KWaylandServer;
21
22 class TestWaylandServerDisplay : public QObject
23 {
24 Q_OBJECT
25 private Q_SLOTS:
26 void testSocketName();
27 void testStartStop();
28 void testAddRemoveOutput();
29 void testClientConnection();
30 void testConnectNoSocket();
31 void testOutputManagement();
32 void testAutoSocketName();
33 };
34
testSocketName()35 void TestWaylandServerDisplay::testSocketName()
36 {
37 Display display;
38 QSignalSpy changedSpy(&display, &Display::socketNamesChanged);
39 QVERIFY(changedSpy.isValid());
40 QCOMPARE(display.socketNames(), QStringList());
41 const QString testSName = QStringLiteral("fooBar");
42 display.addSocketName(testSName);
43 QCOMPARE(display.socketNames(), QStringList{testSName});
44 QCOMPARE(changedSpy.count(), 1);
45
46 // changing to same name again should not emit signal
47 display.addSocketName(testSName);
48 QCOMPARE(changedSpy.count(), 1);
49 }
50
testStartStop()51 void TestWaylandServerDisplay::testStartStop()
52 {
53 const QString testSocketName = QStringLiteral("kwin-wayland-server-display-test-0");
54 QDir runtimeDir(qgetenv("XDG_RUNTIME_DIR"));
55 QVERIFY(runtimeDir.exists());
56 QVERIFY(!runtimeDir.exists(testSocketName));
57
58 QScopedPointer<Display> display(new Display);
59 QSignalSpy runningSpy(display.data(), SIGNAL(runningChanged(bool)));
60 QVERIFY(runningSpy.isValid());
61 display->addSocketName(testSocketName);
62 QVERIFY(!display->isRunning());
63 display->start();
64 // QVERIFY(runningSpy.wait());
65 QCOMPARE(runningSpy.count(), 1);
66 QVERIFY(runningSpy.first().first().toBool());
67 QVERIFY(display->isRunning());
68 QVERIFY(runtimeDir.exists(testSocketName));
69
70 display.reset();
71 QVERIFY(!runtimeDir.exists(testSocketName));
72 }
73
testAddRemoveOutput()74 void TestWaylandServerDisplay::testAddRemoveOutput()
75 {
76 Display display;
77 display.addSocketName(QStringLiteral("kwin-wayland-server-display-test-output-0"));
78 display.start();
79
80 OutputInterface *output = new OutputInterface(&display);
81 QCOMPARE(display.outputs().size(), 1);
82 QCOMPARE(display.outputs().first(), output);
83
84 delete output;
85 QVERIFY(display.outputs().isEmpty());
86 }
87
testClientConnection()88 void TestWaylandServerDisplay::testClientConnection()
89 {
90 Display display;
91 display.addSocketName(QStringLiteral("kwin-wayland-server-display-test-client-connection"));
92 display.start();
93 QSignalSpy connectedSpy(&display, SIGNAL(clientConnected(KWaylandServer::ClientConnection *)));
94 QVERIFY(connectedSpy.isValid());
95 QSignalSpy disconnectedSpy(&display, SIGNAL(clientDisconnected(KWaylandServer::ClientConnection *)));
96 QVERIFY(disconnectedSpy.isValid());
97
98 int sv[2];
99 QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0);
100
101 auto client = wl_client_create(display, sv[0]);
102 QVERIFY(client);
103
104 QVERIFY(connectedSpy.isEmpty());
105 QVERIFY(display.connections().isEmpty());
106 ClientConnection *connection = display.getConnection(client);
107 QVERIFY(connection);
108 QCOMPARE(connection->client(), client);
109 if (getuid() == 0) {
110 QEXPECT_FAIL("", "Please don't run test as root", Continue);
111 }
112 QVERIFY(connection->userId() != 0);
113 if (getgid() == 0) {
114 QEXPECT_FAIL("", "Please don't run test as root", Continue);
115 }
116 QVERIFY(connection->groupId() != 0);
117 QVERIFY(connection->processId() != 0);
118 QCOMPARE(connection->display(), &display);
119 QCOMPARE(connection->executablePath(), QCoreApplication::applicationFilePath());
120 QCOMPARE((wl_client *)*connection, client);
121 const ClientConnection &constRef = *connection;
122 QCOMPARE((wl_client *)constRef, client);
123 QCOMPARE(connectedSpy.count(), 1);
124 QCOMPARE(connectedSpy.first().first().value<ClientConnection *>(), connection);
125 QCOMPARE(display.connections().count(), 1);
126 QCOMPARE(display.connections().first(), connection);
127
128 QCOMPARE(connection, display.getConnection(client));
129 QCOMPARE(connectedSpy.count(), 1);
130
131 // create a second client
132 int sv2[2];
133 QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv2) >= 0);
134 auto client2 = display.createClient(sv2[0]);
135 QVERIFY(client2);
136 ClientConnection *connection2 = display.getConnection(client2->client());
137 QVERIFY(connection2);
138 QCOMPARE(connection2, client2);
139 QCOMPARE(connectedSpy.count(), 2);
140 QCOMPARE(connectedSpy.first().first().value<ClientConnection *>(), connection);
141 QCOMPARE(connectedSpy.last().first().value<ClientConnection *>(), connection2);
142 QCOMPARE(connectedSpy.last().first().value<ClientConnection *>(), client2);
143 QCOMPARE(display.connections().count(), 2);
144 QCOMPARE(display.connections().first(), connection);
145 QCOMPARE(display.connections().last(), connection2);
146 QCOMPARE(display.connections().last(), client2);
147
148 // and destroy
149 QVERIFY(disconnectedSpy.isEmpty());
150 wl_client_destroy(client);
151 QCOMPARE(disconnectedSpy.count(), 1);
152 QSignalSpy clientDestroyedSpy(client2, &QObject::destroyed);
153 QVERIFY(clientDestroyedSpy.isValid());
154 client2->destroy();
155 QVERIFY(clientDestroyedSpy.wait());
156 QCOMPARE(disconnectedSpy.count(), 2);
157 close(sv[0]);
158 close(sv[1]);
159 close(sv2[0]);
160 close(sv2[1]);
161 QVERIFY(display.connections().isEmpty());
162 }
163
testConnectNoSocket()164 void TestWaylandServerDisplay::testConnectNoSocket()
165 {
166 Display display;
167 display.start();
168 QVERIFY(display.isRunning());
169
170 // let's try connecting a client
171 int sv[2];
172 QVERIFY(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) >= 0);
173 auto client = display.createClient(sv[0]);
174 QVERIFY(client);
175
176 wl_client_destroy(client->client());
177 close(sv[0]);
178 close(sv[1]);
179 }
180
testOutputManagement()181 void TestWaylandServerDisplay::testOutputManagement()
182 {
183 Display display;
184 display.addSocketName("kwayland-test-0");
185 display.start();
186 new OutputManagementV2Interface(&display, this);
187 }
188
testAutoSocketName()189 void TestWaylandServerDisplay::testAutoSocketName()
190 {
191 QTemporaryDir runtimeDir;
192 QVERIFY(runtimeDir.isValid());
193 QVERIFY(qputenv("XDG_RUNTIME_DIR", runtimeDir.path().toUtf8()));
194
195 Display display0;
196 QSignalSpy socketNameChangedSpy0(&display0, &Display::socketNamesChanged);
197 QVERIFY(socketNameChangedSpy0.isValid());
198 QVERIFY(display0.addSocketName());
199 display0.start();
200 QVERIFY(display0.isRunning());
201 QCOMPARE(socketNameChangedSpy0.count(), 1);
202
203 Display display1;
204 QSignalSpy socketNameChangedSpy1(&display1, &Display::socketNamesChanged);
205 QVERIFY(socketNameChangedSpy1.isValid());
206 QVERIFY(display1.addSocketName());
207 display1.start();
208 QVERIFY(display1.isRunning());
209 QCOMPARE(socketNameChangedSpy1.count(), 1);
210 }
211
212 QTEST_GUILESS_MAIN(TestWaylandServerDisplay)
213 #include "test_display.moc"
214