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