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 // KWayland
9 #include "../../src/server/compositor_interface.h"
10 #include "../../src/server/datadevicemanager_interface.h"
11 #include "../../src/server/datasource_interface.h"
12 #include "../../src/server/display.h"
13 #include "../../src/server/pointer_interface.h"
14 #include "../../src/server/seat_interface.h"
15 #include "../../src/server/surface_interface.h"
16 #include "KWayland/Client/compositor.h"
17 #include "KWayland/Client/connection_thread.h"
18 #include "KWayland/Client/datadevice.h"
19 #include "KWayland/Client/datadevicemanager.h"
20 #include "KWayland/Client/datasource.h"
21 #include "KWayland/Client/event_queue.h"
22 #include "KWayland/Client/keyboard.h"
23 #include "KWayland/Client/pointer.h"
24 #include "KWayland/Client/registry.h"
25 #include "KWayland/Client/seat.h"
26 #include "KWayland/Client/surface.h"
27 // Wayland
28 #include <wayland-client.h>
29
30 #include <unistd.h>
31
32 class TestDataDevice : public QObject
33 {
34 Q_OBJECT
35 private Q_SLOTS:
36 void init();
37 void cleanup();
38
39 void testCreate();
40 void testDrag_data();
41 void testDrag();
42 void testDragInternally_data();
43 void testDragInternally();
44 void testSetSelection();
45 void testSendSelectionOnSeat();
46 void testReplaceSource();
47 void testDestroy();
48
49 private:
50 KWaylandServer::Display *m_display = nullptr;
51 KWaylandServer::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
52 KWaylandServer::CompositorInterface *m_compositorInterface = nullptr;
53 KWaylandServer::SeatInterface *m_seatInterface = nullptr;
54 KWayland::Client::ConnectionThread *m_connection = nullptr;
55 KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr;
56 KWayland::Client::Compositor *m_compositor = nullptr;
57 KWayland::Client::Seat *m_seat = nullptr;
58 KWayland::Client::EventQueue *m_queue = nullptr;
59 QThread *m_thread = nullptr;
60 };
61
62 static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0");
63
init()64 void TestDataDevice::init()
65 {
66 qRegisterMetaType<KWaylandServer::DataSourceInterface *>();
67 using namespace KWaylandServer;
68 delete m_display;
69 m_display = new Display(this);
70 m_display->addSocketName(s_socketName);
71 m_display->start();
72 QVERIFY(m_display->isRunning());
73
74 // setup connection
75 m_connection = new KWayland::Client::ConnectionThread;
76 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
77 m_connection->setSocketName(s_socketName);
78
79 m_thread = new QThread(this);
80 m_connection->moveToThread(m_thread);
81 m_thread->start();
82
83 m_connection->initConnection();
84 QVERIFY(connectedSpy.wait());
85
86 m_queue = new KWayland::Client::EventQueue(this);
87 QVERIFY(!m_queue->isValid());
88 m_queue->setup(m_connection);
89 QVERIFY(m_queue->isValid());
90
91 KWayland::Client::Registry registry;
92 QSignalSpy dataDeviceManagerSpy(®istry, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
93 QVERIFY(dataDeviceManagerSpy.isValid());
94 QSignalSpy seatSpy(®istry, &KWayland::Client::Registry::seatAnnounced);
95 QVERIFY(seatSpy.isValid());
96 QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced);
97 QVERIFY(compositorSpy.isValid());
98 QVERIFY(!registry.eventQueue());
99 registry.setEventQueue(m_queue);
100 QCOMPARE(registry.eventQueue(), m_queue);
101 registry.create(m_connection->display());
102 QVERIFY(registry.isValid());
103 registry.setup();
104
105 m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display);
106
107 QVERIFY(dataDeviceManagerSpy.wait());
108 m_dataDeviceManager =
109 registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>(), this);
110
111 m_seatInterface = new SeatInterface(m_display, m_display);
112 m_seatInterface->setHasPointer(true);
113
114 QVERIFY(seatSpy.wait());
115 m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
116 QVERIFY(m_seat->isValid());
117 QSignalSpy pointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
118 QVERIFY(pointerChangedSpy.isValid());
119 QVERIFY(pointerChangedSpy.wait());
120
121 m_compositorInterface = new CompositorInterface(m_display, m_display);
122 QVERIFY(compositorSpy.wait());
123 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
124 QVERIFY(m_compositor->isValid());
125 }
126
cleanup()127 void TestDataDevice::cleanup()
128 {
129 if (m_dataDeviceManager) {
130 delete m_dataDeviceManager;
131 m_dataDeviceManager = nullptr;
132 }
133 if (m_seat) {
134 delete m_seat;
135 m_seat = nullptr;
136 }
137 if (m_compositor) {
138 delete m_compositor;
139 m_compositor = nullptr;
140 }
141 if (m_queue) {
142 delete m_queue;
143 m_queue = nullptr;
144 }
145 if (m_thread) {
146 m_thread->quit();
147 m_thread->wait();
148 delete m_thread;
149 m_thread = nullptr;
150 }
151 delete m_connection;
152 m_connection = nullptr;
153
154 delete m_display;
155 m_display = nullptr;
156 }
157
testCreate()158 void TestDataDevice::testCreate()
159 {
160 using namespace KWayland::Client;
161 using namespace KWaylandServer;
162
163 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataDeviceCreated);
164 QVERIFY(dataDeviceCreatedSpy.isValid());
165
166 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
167 QVERIFY(dataDevice->isValid());
168
169 QVERIFY(dataDeviceCreatedSpy.wait());
170 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
171 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
172 QVERIFY(deviceInterface);
173 QCOMPARE(deviceInterface->seat(), m_seatInterface);
174 QVERIFY(!deviceInterface->selection());
175
176 // this will probably fail, we need to make a selection client side
177 QVERIFY(!m_seatInterface->selection());
178 m_seatInterface->setSelection(deviceInterface->selection());
179 QCOMPARE(m_seatInterface->selection(), deviceInterface->selection());
180
181 // and destroy
182 QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed);
183 QVERIFY(destroyedSpy.isValid());
184 dataDevice.reset();
185 QVERIFY(destroyedSpy.wait());
186 QVERIFY(!m_seatInterface->selection());
187 }
188
testDrag_data()189 void TestDataDevice::testDrag_data()
190 {
191 QTest::addColumn<bool>("hasGrab");
192 QTest::addColumn<bool>("hasPointerFocus");
193 QTest::addColumn<bool>("success");
194
195 QTest::newRow("grab and focus") << true << true << true;
196 QTest::newRow("no grab") << false << true << false;
197 QTest::newRow("no focus") << true << false << false;
198 QTest::newRow("no grab, no focus") << false << false << false;
199 }
200
testDrag()201 void TestDataDevice::testDrag()
202 {
203 using namespace KWayland::Client;
204 using namespace KWaylandServer;
205 QScopedPointer<Pointer> pointer(m_seat->createPointer());
206
207 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataDeviceCreated);
208 QVERIFY(dataDeviceCreatedSpy.isValid());
209
210 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
211 QVERIFY(dataDevice->isValid());
212
213 QVERIFY(dataDeviceCreatedSpy.wait());
214 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
215 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
216 QVERIFY(deviceInterface);
217
218 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataSourceCreated);
219 QVERIFY(dataDeviceCreatedSpy.isValid());
220
221 QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
222 QVERIFY(dataSource->isValid());
223
224 QVERIFY(dataSourceCreatedSpy.wait());
225 QCOMPARE(dataSourceCreatedSpy.count(), 1);
226 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
227 QVERIFY(sourceInterface);
228
229 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWaylandServer::CompositorInterface::surfaceCreated);
230 QVERIFY(surfaceCreatedSpy.isValid());
231
232 QScopedPointer<Surface> surface(m_compositor->createSurface());
233 QVERIFY(surface->isValid());
234
235 QVERIFY(surfaceCreatedSpy.wait());
236 QCOMPARE(surfaceCreatedSpy.count(), 1);
237 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
238
239 // now we have all we need to start a drag operation
240 QSignalSpy dragStartedSpy(deviceInterface, &KWaylandServer::DataDeviceInterface::dragStarted);
241 QVERIFY(dragStartedSpy.isValid());
242
243 // first we need to fake the pointer enter
244 QFETCH(bool, hasGrab);
245 QFETCH(bool, hasPointerFocus);
246 QFETCH(bool, success);
247 if (!hasGrab) {
248 // in case we don't have grab, still generate a pointer serial to make it more interesting
249 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
250 m_seatInterface->notifyPointerFrame();
251 }
252 if (hasPointerFocus) {
253 m_seatInterface->setFocusedPointerSurface(surfaceInterface);
254 }
255 if (hasGrab) {
256 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
257 m_seatInterface->notifyPointerFrame();
258 }
259
260 // TODO: This test would be better, if it could also test that a client trying to guess
261 // the last serial of a different client can't start a drag.
262 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
263
264 QCoreApplication::processEvents();
265 // finally start the drag
266 dataDevice->startDrag(pointerButtonSerial, dataSource.data(), surface.data());
267 QCOMPARE(dragStartedSpy.wait(500), success);
268 QCOMPARE(!dragStartedSpy.isEmpty(), success);
269 QCOMPARE(m_seatInterface->dragSource(), success ? sourceInterface : nullptr);
270 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
271 QVERIFY(!m_seatInterface->dragIcon());
272 }
273
testDragInternally_data()274 void TestDataDevice::testDragInternally_data()
275 {
276 QTest::addColumn<bool>("hasGrab");
277 QTest::addColumn<bool>("hasPointerFocus");
278 QTest::addColumn<bool>("success");
279
280 QTest::newRow("grab and focus") << true << true << true;
281 QTest::newRow("no grab") << false << true << false;
282 QTest::newRow("no focus") << true << false << false;
283 QTest::newRow("no grab, no focus") << false << false << false;
284 }
285
testDragInternally()286 void TestDataDevice::testDragInternally()
287 {
288 using namespace KWayland::Client;
289 using namespace KWaylandServer;
290 QScopedPointer<Pointer> pointer(m_seat->createPointer());
291
292 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataDeviceCreated);
293 QVERIFY(dataDeviceCreatedSpy.isValid());
294
295 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
296 QVERIFY(dataDevice->isValid());
297
298 QVERIFY(dataDeviceCreatedSpy.wait());
299 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
300 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
301 QVERIFY(deviceInterface);
302
303 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWaylandServer::CompositorInterface::surfaceCreated);
304 QVERIFY(surfaceCreatedSpy.isValid());
305
306 QScopedPointer<Surface> surface(m_compositor->createSurface());
307 QVERIFY(surface->isValid());
308
309 QVERIFY(surfaceCreatedSpy.wait());
310 QCOMPARE(surfaceCreatedSpy.count(), 1);
311 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
312
313 QScopedPointer<Surface> iconSurface(m_compositor->createSurface());
314 QVERIFY(iconSurface->isValid());
315
316 QVERIFY(surfaceCreatedSpy.wait());
317 QCOMPARE(surfaceCreatedSpy.count(), 2);
318 auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
319
320 // now we have all we need to start a drag operation
321 QSignalSpy dragStartedSpy(deviceInterface, &KWaylandServer::DataDeviceInterface::dragStarted);
322 QVERIFY(dragStartedSpy.isValid());
323
324 // first we need to fake the pointer enter
325 QFETCH(bool, hasGrab);
326 QFETCH(bool, hasPointerFocus);
327 QFETCH(bool, success);
328 if (!hasGrab) {
329 // in case we don't have grab, still generate a pointer serial to make it more interesting
330 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
331 m_seatInterface->notifyPointerFrame();
332 }
333 if (hasPointerFocus) {
334 m_seatInterface->setFocusedPointerSurface(surfaceInterface);
335 }
336 if (hasGrab) {
337 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
338 m_seatInterface->notifyPointerFrame();
339 }
340
341 // TODO: This test would be better, if it could also test that a client trying to guess
342 // the last serial of a different client can't start a drag.
343 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
344
345 QCoreApplication::processEvents();
346 // finally start the internal drag
347 dataDevice->startDragInternally(pointerButtonSerial, surface.data(), iconSurface.data());
348 QCOMPARE(dragStartedSpy.wait(500), success);
349 QCOMPARE(!dragStartedSpy.isEmpty(), success);
350 QVERIFY(!m_seatInterface->dragSource());
351 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
352
353 if (success) {
354 QCOMPARE(m_seatInterface->dragIcon()->surface(), iconSurfaceInterface);
355 } else {
356 QCOMPARE(m_seatInterface->dragIcon(), nullptr);
357 }
358 }
359
testSetSelection()360 void TestDataDevice::testSetSelection()
361 {
362 using namespace KWayland::Client;
363 using namespace KWaylandServer;
364 QScopedPointer<Pointer> pointer(m_seat->createPointer());
365
366 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataDeviceCreated);
367 QVERIFY(dataDeviceCreatedSpy.isValid());
368
369 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
370 QVERIFY(dataDevice->isValid());
371
372 QVERIFY(dataDeviceCreatedSpy.wait());
373 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
374 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
375 QVERIFY(deviceInterface);
376
377 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWaylandServer::DataDeviceManagerInterface::dataSourceCreated);
378 QVERIFY(dataDeviceCreatedSpy.isValid());
379
380 QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
381 QVERIFY(dataSource->isValid());
382 dataSource->offer(QStringLiteral("text/plain"));
383
384 QVERIFY(dataSourceCreatedSpy.wait());
385 QCOMPARE(dataSourceCreatedSpy.count(), 1);
386 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
387 QVERIFY(sourceInterface);
388
389 // everything setup, now we can test setting the selection
390 QSignalSpy selectionChangedSpy(deviceInterface, &KWaylandServer::DataDeviceInterface::selectionChanged);
391 QVERIFY(selectionChangedSpy.isValid());
392 QSignalSpy selectionClearedSpy(deviceInterface, &KWaylandServer::DataDeviceInterface::selectionCleared);
393 QVERIFY(selectionClearedSpy.isValid());
394
395 QVERIFY(!deviceInterface->selection());
396 dataDevice->setSelection(1, dataSource.data());
397 QVERIFY(selectionChangedSpy.wait());
398 QCOMPARE(selectionChangedSpy.count(), 1);
399 QCOMPARE(selectionClearedSpy.count(), 0);
400 QCOMPARE(selectionChangedSpy.first().first().value<DataSourceInterface *>(), sourceInterface);
401 QCOMPARE(deviceInterface->selection(), sourceInterface);
402
403 // send selection to datadevice
404 QSignalSpy selectionOfferedSpy(dataDevice.data(), &KWayland::Client::DataDevice::selectionOffered);
405 QVERIFY(selectionOfferedSpy.isValid());
406 deviceInterface->sendSelection(deviceInterface->selection());
407 QVERIFY(selectionOfferedSpy.wait());
408 QCOMPARE(selectionOfferedSpy.count(), 1);
409 auto dataOffer = selectionOfferedSpy.first().first().value<DataOffer *>();
410 QVERIFY(dataOffer);
411 QCOMPARE(dataOffer->offeredMimeTypes().count(), 1);
412 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
413
414 // sending a new mimetype to the selection, should be announced in the offer
415 QSignalSpy mimeTypeAddedSpy(dataOffer, &KWayland::Client::DataOffer::mimeTypeOffered);
416 QVERIFY(mimeTypeAddedSpy.isValid());
417 dataSource->offer(QStringLiteral("text/html"));
418 QVERIFY(mimeTypeAddedSpy.wait());
419 QCOMPARE(mimeTypeAddedSpy.count(), 1);
420 QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html"));
421 QCOMPARE(dataOffer->offeredMimeTypes().count(), 2);
422 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
423 QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html"));
424
425 // now clear the selection
426 dataDevice->clearSelection(1);
427 QVERIFY(selectionClearedSpy.wait());
428 QCOMPARE(selectionChangedSpy.count(), 1);
429 QCOMPARE(selectionClearedSpy.count(), 1);
430 QVERIFY(!deviceInterface->selection());
431
432 // set another selection
433 dataDevice->setSelection(2, dataSource.data());
434 QVERIFY(selectionChangedSpy.wait());
435 // now unbind the dataDevice
436 QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed);
437 QVERIFY(unboundSpy.isValid());
438 dataDevice.reset();
439 QVERIFY(unboundSpy.wait());
440 }
441
testSendSelectionOnSeat()442 void TestDataDevice::testSendSelectionOnSeat()
443 {
444 // this test verifies that the selection is sent when setting a focused keyboard
445 using namespace KWayland::Client;
446 using namespace KWaylandServer;
447 // first add keyboard support to Seat
448 QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged);
449 QVERIFY(keyboardChangedSpy.isValid());
450 m_seatInterface->setHasKeyboard(true);
451 QVERIFY(keyboardChangedSpy.wait());
452 // now create DataDevice, Keyboard and a Surface
453 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
454 QVERIFY(dataDeviceCreatedSpy.isValid());
455 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
456 QVERIFY(dataDevice->isValid());
457 QVERIFY(dataDeviceCreatedSpy.wait());
458 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
459 QVERIFY(serverDataDevice);
460 QScopedPointer<Keyboard> keyboard(m_seat->createKeyboard());
461 QVERIFY(keyboard->isValid());
462 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
463 QVERIFY(surfaceCreatedSpy.isValid());
464 QScopedPointer<Surface> surface(m_compositor->createSurface());
465 QVERIFY(surface->isValid());
466 QVERIFY(surfaceCreatedSpy.wait());
467
468 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
469 QVERIFY(serverSurface);
470 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
471
472 // now set the selection
473 QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
474 QVERIFY(dataSource->isValid());
475 dataSource->offer(QStringLiteral("text/plain"));
476 dataDevice->setSelection(1, dataSource.data());
477 // we should get a selection offered for that on the data device
478 QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered);
479 QVERIFY(selectionOfferedSpy.isValid());
480 QVERIFY(selectionOfferedSpy.wait());
481 QCOMPARE(selectionOfferedSpy.count(), 1);
482
483 // now unfocus the keyboard
484 m_seatInterface->setFocusedKeyboardSurface(nullptr);
485 // if setting the same surface again, we should get another offer
486 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
487 QVERIFY(selectionOfferedSpy.wait());
488 QCOMPARE(selectionOfferedSpy.count(), 2);
489
490 // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd
491 m_seatInterface->setFocusedKeyboardSurface(nullptr);
492 QSignalSpy unboundSpy(serverDataDevice, &QObject::destroyed);
493 QVERIFY(unboundSpy.isValid());
494 dataDevice.reset();
495 QVERIFY(unboundSpy.wait());
496 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
497 }
498
testReplaceSource()499 void TestDataDevice::testReplaceSource()
500 {
501 // this test verifies that replacing a data source cancels the previous source
502 using namespace KWayland::Client;
503 using namespace KWaylandServer;
504 // first add keyboard support to Seat
505 QSignalSpy keyboardChangedSpy(m_seat, &Seat::hasKeyboardChanged);
506 QVERIFY(keyboardChangedSpy.isValid());
507 m_seatInterface->setHasKeyboard(true);
508 QVERIFY(keyboardChangedSpy.wait());
509 // now create DataDevice, Keyboard and a Surface
510 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
511 QVERIFY(dataDeviceCreatedSpy.isValid());
512 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
513 QVERIFY(dataDevice->isValid());
514 QVERIFY(dataDeviceCreatedSpy.wait());
515 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
516 QVERIFY(serverDataDevice);
517 QScopedPointer<Keyboard> keyboard(m_seat->createKeyboard());
518 QVERIFY(keyboard->isValid());
519 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
520 QVERIFY(surfaceCreatedSpy.isValid());
521 QScopedPointer<Surface> surface(m_compositor->createSurface());
522 QVERIFY(surface->isValid());
523 QVERIFY(surfaceCreatedSpy.wait());
524
525 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
526 QVERIFY(serverSurface);
527 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
528
529 // now set the selection
530 QScopedPointer<DataSource> dataSource(m_dataDeviceManager->createDataSource());
531 QVERIFY(dataSource->isValid());
532 dataSource->offer(QStringLiteral("text/plain"));
533 dataDevice->setSelection(1, dataSource.data());
534 QSignalSpy sourceCancelledSpy(dataSource.data(), &DataSource::cancelled);
535 QVERIFY(sourceCancelledSpy.isValid());
536 // we should get a selection offered for that on the data device
537 QSignalSpy selectionOfferedSpy(dataDevice.data(), &DataDevice::selectionOffered);
538 QVERIFY(selectionOfferedSpy.isValid());
539 QVERIFY(selectionOfferedSpy.wait());
540 QCOMPARE(selectionOfferedSpy.count(), 1);
541
542 // create a second data source and replace previous one
543 QScopedPointer<DataSource> dataSource2(m_dataDeviceManager->createDataSource());
544 QVERIFY(dataSource2->isValid());
545 dataSource2->offer(QStringLiteral("text/plain"));
546 QSignalSpy sourceCancelled2Spy(dataSource2.data(), &DataSource::cancelled);
547 QVERIFY(sourceCancelled2Spy.isValid());
548 dataDevice->setSelection(1, dataSource2.data());
549 QCOMPARE(selectionOfferedSpy.count(), 1);
550 QVERIFY(sourceCancelledSpy.wait());
551 QCOMPARE(selectionOfferedSpy.count(), 2);
552 QVERIFY(sourceCancelled2Spy.isEmpty());
553
554 // replace the data source with itself, ensure that it did not get cancelled
555 dataDevice->setSelection(1, dataSource2.data());
556 QVERIFY(!sourceCancelled2Spy.wait(500));
557 QCOMPARE(selectionOfferedSpy.count(), 2);
558 QVERIFY(sourceCancelled2Spy.isEmpty());
559
560 // create a new DataDevice and replace previous one
561 QScopedPointer<DataDevice> dataDevice2(m_dataDeviceManager->getDataDevice(m_seat));
562 QVERIFY(dataDevice2->isValid());
563 QScopedPointer<DataSource> dataSource3(m_dataDeviceManager->createDataSource());
564 QVERIFY(dataSource3->isValid());
565 dataSource3->offer(QStringLiteral("text/plain"));
566 dataDevice2->setSelection(1, dataSource3.data());
567 QVERIFY(sourceCancelled2Spy.wait());
568
569 // try to crash by first destroying dataSource3 and setting a new DataSource
570 QScopedPointer<DataSource> dataSource4(m_dataDeviceManager->createDataSource());
571 QVERIFY(dataSource4->isValid());
572 dataSource4->offer(QStringLiteral("text/plain"));
573 dataSource3.reset();
574 dataDevice2->setSelection(1, dataSource4.data());
575 QVERIFY(selectionOfferedSpy.wait());
576
577 auto dataOffer = selectionOfferedSpy.last()[0].value<DataOffer *>();
578
579 // try to crash by destroying the data source, then requesting data
580 dataSource4.reset();
581 int pipeFds[2] = {0, 0};
582 QVERIFY(pipe(pipeFds) == 0);
583
584 dataOffer->receive(QStringLiteral("text/plain"), pipeFds[1]);
585 close(pipeFds[1]);
586
587 // spin the event loop, nothing should explode
588 QTest::qWait(10);
589
590 close(pipeFds[0]);
591 }
592
testDestroy()593 void TestDataDevice::testDestroy()
594 {
595 using namespace KWayland::Client;
596
597 QScopedPointer<DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
598 QVERIFY(dataDevice->isValid());
599
600 connect(m_connection, &ConnectionThread::connectionDied, m_dataDeviceManager, &DataDeviceManager::destroy);
601 connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy);
602 connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy);
603 connect(m_connection, &ConnectionThread::connectionDied, dataDevice.data(), &DataDevice::destroy);
604 connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy);
605
606 QSignalSpy connectionDiedSpy(m_connection, &KWayland::Client::ConnectionThread::connectionDied);
607 QVERIFY(connectionDiedSpy.isValid());
608 delete m_display;
609 m_display = nullptr;
610 QVERIFY(connectionDiedSpy.wait());
611
612 // now the data device should be destroyed;
613 QVERIFY(!dataDevice->isValid());
614
615 // calling destroy again should not fail
616 dataDevice->destroy();
617 }
618
619 QTEST_GUILESS_MAIN(TestDataDevice)
620 #include "test_datadevice.moc"
621