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(&registry, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
93     QVERIFY(dataDeviceManagerSpy.isValid());
94     QSignalSpy seatSpy(&registry, &KWayland::Client::Registry::seatAnnounced);
95     QVERIFY(seatSpy.isValid());
96     QSignalSpy compositorSpy(&registry, &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