File indexing completed on 2024-11-10 04:56:15

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 // Qt
0007 #include <QMimeType>
0008 #include <QSignalSpy>
0009 #include <QTest>
0010 
0011 #include "wayland/compositor.h"
0012 #include "wayland/datadevicemanager.h"
0013 #include "wayland/datasource.h"
0014 #include "wayland/display.h"
0015 #include "wayland/pointer.h"
0016 #include "wayland/seat.h"
0017 #include "wayland/surface.h"
0018 
0019 // KWayland
0020 #include "KWayland/Client/compositor.h"
0021 #include "KWayland/Client/connection_thread.h"
0022 #include "KWayland/Client/datadevice.h"
0023 #include "KWayland/Client/datadevicemanager.h"
0024 #include "KWayland/Client/datasource.h"
0025 #include "KWayland/Client/event_queue.h"
0026 #include "KWayland/Client/keyboard.h"
0027 #include "KWayland/Client/pointer.h"
0028 #include "KWayland/Client/registry.h"
0029 #include "KWayland/Client/seat.h"
0030 #include "KWayland/Client/surface.h"
0031 
0032 // Wayland
0033 #include <wayland-client.h>
0034 
0035 #include <unistd.h>
0036 
0037 class TestDataDevice : public QObject
0038 {
0039     Q_OBJECT
0040 private Q_SLOTS:
0041     void init();
0042     void cleanup();
0043 
0044     void testCreate();
0045     void testDrag_data();
0046     void testDrag();
0047     void testDragInternally_data();
0048     void testDragInternally();
0049     void testSetSelection();
0050     void testSendSelectionOnSeat();
0051     void testReplaceSource();
0052 
0053 private:
0054     KWin::Display *m_display = nullptr;
0055     KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
0056     KWin::CompositorInterface *m_compositorInterface = nullptr;
0057     KWin::SeatInterface *m_seatInterface = nullptr;
0058     KWayland::Client::ConnectionThread *m_connection = nullptr;
0059     KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr;
0060     KWayland::Client::Compositor *m_compositor = nullptr;
0061     KWayland::Client::Seat *m_seat = nullptr;
0062     KWayland::Client::EventQueue *m_queue = nullptr;
0063     QThread *m_thread = nullptr;
0064 };
0065 
0066 static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0");
0067 
0068 void TestDataDevice::init()
0069 {
0070     qRegisterMetaType<KWin::DataSourceInterface *>();
0071     using namespace KWin;
0072     delete m_display;
0073     m_display = new KWin::Display(this);
0074     m_display->addSocketName(s_socketName);
0075     m_display->start();
0076     QVERIFY(m_display->isRunning());
0077 
0078     // setup connection
0079     m_connection = new KWayland::Client::ConnectionThread;
0080     QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
0081     m_connection->setSocketName(s_socketName);
0082 
0083     m_thread = new QThread(this);
0084     m_connection->moveToThread(m_thread);
0085     m_thread->start();
0086 
0087     m_connection->initConnection();
0088     QVERIFY(connectedSpy.wait());
0089 
0090     m_queue = new KWayland::Client::EventQueue(this);
0091     QVERIFY(!m_queue->isValid());
0092     m_queue->setup(m_connection);
0093     QVERIFY(m_queue->isValid());
0094 
0095     KWayland::Client::Registry registry;
0096     QSignalSpy dataDeviceManagerSpy(&registry, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
0097     QSignalSpy seatSpy(&registry, &KWayland::Client::Registry::seatAnnounced);
0098     QSignalSpy compositorSpy(&registry, &KWayland::Client::Registry::compositorAnnounced);
0099     QVERIFY(!registry.eventQueue());
0100     registry.setEventQueue(m_queue);
0101     QCOMPARE(registry.eventQueue(), m_queue);
0102     registry.create(m_connection->display());
0103     QVERIFY(registry.isValid());
0104     registry.setup();
0105 
0106     m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display);
0107 
0108     QVERIFY(dataDeviceManagerSpy.wait());
0109     m_dataDeviceManager =
0110         registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>(), this);
0111 
0112     m_seatInterface = new SeatInterface(m_display, m_display);
0113     m_seatInterface->setHasPointer(true);
0114 
0115     QVERIFY(seatSpy.wait());
0116     m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
0117     QVERIFY(m_seat->isValid());
0118     QSignalSpy pointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
0119     QVERIFY(pointerChangedSpy.wait());
0120 
0121     m_compositorInterface = new CompositorInterface(m_display, m_display);
0122     QVERIFY(compositorSpy.wait());
0123     m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
0124     QVERIFY(m_compositor->isValid());
0125 }
0126 
0127 void TestDataDevice::cleanup()
0128 {
0129     if (m_dataDeviceManager) {
0130         delete m_dataDeviceManager;
0131         m_dataDeviceManager = nullptr;
0132     }
0133     if (m_seat) {
0134         delete m_seat;
0135         m_seat = nullptr;
0136     }
0137     if (m_compositor) {
0138         delete m_compositor;
0139         m_compositor = nullptr;
0140     }
0141     if (m_queue) {
0142         delete m_queue;
0143         m_queue = nullptr;
0144     }
0145     if (m_thread) {
0146         m_thread->quit();
0147         m_thread->wait();
0148         delete m_thread;
0149         m_thread = nullptr;
0150     }
0151     delete m_connection;
0152     m_connection = nullptr;
0153 
0154     delete m_display;
0155     m_display = nullptr;
0156 }
0157 
0158 void TestDataDevice::testCreate()
0159 {
0160     using namespace KWin;
0161 
0162     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
0163 
0164     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0165     QVERIFY(dataDevice->isValid());
0166 
0167     QVERIFY(dataDeviceCreatedSpy.wait());
0168     QCOMPARE(dataDeviceCreatedSpy.count(), 1);
0169     auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0170     QVERIFY(deviceInterface);
0171     QCOMPARE(deviceInterface->seat(), m_seatInterface);
0172     QVERIFY(!deviceInterface->selection());
0173 
0174     // this will probably fail, we need to make a selection client side
0175     QVERIFY(!m_seatInterface->selection());
0176     m_seatInterface->setSelection(deviceInterface->selection());
0177     QCOMPARE(m_seatInterface->selection(), deviceInterface->selection());
0178 
0179     // and destroy
0180     QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed);
0181     dataDevice.reset();
0182     QVERIFY(destroyedSpy.wait());
0183     QVERIFY(!m_seatInterface->selection());
0184 }
0185 
0186 void TestDataDevice::testDrag_data()
0187 {
0188     QTest::addColumn<bool>("hasGrab");
0189     QTest::addColumn<bool>("hasPointerFocus");
0190     QTest::addColumn<bool>("success");
0191 
0192     QTest::newRow("grab and focus") << true << true << true;
0193     QTest::newRow("no grab") << false << true << false;
0194     QTest::newRow("no focus") << true << false << false;
0195     QTest::newRow("no grab, no focus") << false << false << false;
0196 }
0197 
0198 void TestDataDevice::testDrag()
0199 {
0200     using namespace KWin;
0201     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0202 
0203     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
0204 
0205     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0206     QVERIFY(dataDevice->isValid());
0207 
0208     QVERIFY(dataDeviceCreatedSpy.wait());
0209     QCOMPARE(dataDeviceCreatedSpy.count(), 1);
0210     auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0211     QVERIFY(deviceInterface);
0212 
0213     QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated);
0214 
0215     std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
0216     QVERIFY(dataSource->isValid());
0217 
0218     QVERIFY(dataSourceCreatedSpy.wait());
0219     QCOMPARE(dataSourceCreatedSpy.count(), 1);
0220     auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
0221     QVERIFY(sourceInterface);
0222 
0223     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
0224 
0225     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
0226     QVERIFY(surface->isValid());
0227 
0228     QVERIFY(surfaceCreatedSpy.wait());
0229     QCOMPARE(surfaceCreatedSpy.count(), 1);
0230     auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0231 
0232     // now we have all we need to start a drag operation
0233     QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted);
0234 
0235     // first we need to fake the pointer enter
0236     QFETCH(bool, hasGrab);
0237     QFETCH(bool, hasPointerFocus);
0238     QFETCH(bool, success);
0239     if (!hasGrab) {
0240         // in case we don't have grab, still generate a pointer serial to make it more interesting
0241         m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
0242         m_seatInterface->notifyPointerFrame();
0243     }
0244     if (hasPointerFocus) {
0245         m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0));
0246     }
0247     if (hasGrab) {
0248         m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
0249         m_seatInterface->notifyPointerFrame();
0250     }
0251 
0252     // TODO: This test would be better, if it could also test that a client trying to guess
0253     //       the last serial of a different client can't start a drag.
0254     const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
0255 
0256     QCoreApplication::processEvents();
0257     // finally start the drag
0258     dataDevice->startDrag(pointerButtonSerial, dataSource.get(), surface.get());
0259     QCOMPARE(dragStartedSpy.wait(500), success);
0260     QCOMPARE(!dragStartedSpy.isEmpty(), success);
0261     QCOMPARE(m_seatInterface->dragSource(), success ? sourceInterface : nullptr);
0262     QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
0263     QVERIFY(!m_seatInterface->dragIcon());
0264 }
0265 
0266 void TestDataDevice::testDragInternally_data()
0267 {
0268     QTest::addColumn<bool>("hasGrab");
0269     QTest::addColumn<bool>("hasPointerFocus");
0270     QTest::addColumn<bool>("success");
0271 
0272     QTest::newRow("grab and focus") << true << true << true;
0273     QTest::newRow("no grab") << false << true << false;
0274     QTest::newRow("no focus") << true << false << false;
0275     QTest::newRow("no grab, no focus") << false << false << false;
0276 }
0277 
0278 void TestDataDevice::testDragInternally()
0279 {
0280     using namespace KWin;
0281     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0282 
0283     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
0284 
0285     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0286     QVERIFY(dataDevice->isValid());
0287 
0288     QVERIFY(dataDeviceCreatedSpy.wait());
0289     QCOMPARE(dataDeviceCreatedSpy.count(), 1);
0290     auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0291     QVERIFY(deviceInterface);
0292 
0293     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
0294 
0295     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
0296     QVERIFY(surface->isValid());
0297 
0298     QVERIFY(surfaceCreatedSpy.wait());
0299     QCOMPARE(surfaceCreatedSpy.count(), 1);
0300     auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0301 
0302     std::unique_ptr<KWayland::Client::Surface> iconSurface(m_compositor->createSurface());
0303     QVERIFY(iconSurface->isValid());
0304 
0305     QVERIFY(surfaceCreatedSpy.wait());
0306     QCOMPARE(surfaceCreatedSpy.count(), 2);
0307     auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
0308 
0309     // now we have all we need to start a drag operation
0310     QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted);
0311 
0312     // first we need to fake the pointer enter
0313     QFETCH(bool, hasGrab);
0314     QFETCH(bool, hasPointerFocus);
0315     QFETCH(bool, success);
0316     if (!hasGrab) {
0317         // in case we don't have grab, still generate a pointer serial to make it more interesting
0318         m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
0319         m_seatInterface->notifyPointerFrame();
0320     }
0321     if (hasPointerFocus) {
0322         m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0));
0323     }
0324     if (hasGrab) {
0325         m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
0326         m_seatInterface->notifyPointerFrame();
0327     }
0328 
0329     // TODO: This test would be better, if it could also test that a client trying to guess
0330     //       the last serial of a different client can't start a drag.
0331     const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
0332 
0333     QCoreApplication::processEvents();
0334     // finally start the internal drag
0335     dataDevice->startDragInternally(pointerButtonSerial, surface.get(), iconSurface.get());
0336     QCOMPARE(dragStartedSpy.wait(500), success);
0337     QCOMPARE(!dragStartedSpy.isEmpty(), success);
0338     QVERIFY(!m_seatInterface->dragSource());
0339     QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
0340 
0341     if (success) {
0342         QCOMPARE(m_seatInterface->dragIcon()->surface(), iconSurfaceInterface);
0343     } else {
0344         QCOMPARE(m_seatInterface->dragIcon(), nullptr);
0345     }
0346 }
0347 
0348 void TestDataDevice::testSetSelection()
0349 {
0350     using namespace KWin;
0351     std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
0352 
0353     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
0354 
0355     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0356     QVERIFY(dataDevice->isValid());
0357 
0358     QVERIFY(dataDeviceCreatedSpy.wait());
0359     QCOMPARE(dataDeviceCreatedSpy.count(), 1);
0360     auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0361     QVERIFY(deviceInterface);
0362 
0363     QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated);
0364 
0365     std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
0366     QVERIFY(dataSource->isValid());
0367     dataSource->offer(QStringLiteral("text/plain"));
0368 
0369     QVERIFY(dataSourceCreatedSpy.wait());
0370     QCOMPARE(dataSourceCreatedSpy.count(), 1);
0371     auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
0372     QVERIFY(sourceInterface);
0373 
0374     // everything setup, now we can test setting the selection
0375     QSignalSpy selectionChangedSpy(deviceInterface, &KWin::DataDeviceInterface::selectionChanged);
0376 
0377     QVERIFY(!deviceInterface->selection());
0378     dataDevice->setSelection(1, dataSource.get());
0379     QVERIFY(selectionChangedSpy.wait());
0380     QCOMPARE(selectionChangedSpy.count(), 1);
0381     QCOMPARE(selectionChangedSpy.first().first().value<DataSourceInterface *>(), sourceInterface);
0382     QCOMPARE(deviceInterface->selection(), sourceInterface);
0383 
0384     // send selection to datadevice
0385     QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
0386     deviceInterface->sendSelection(deviceInterface->selection());
0387     QVERIFY(selectionOfferedSpy.wait());
0388     QCOMPARE(selectionOfferedSpy.count(), 1);
0389     auto dataOffer = selectionOfferedSpy.first().first().value<KWayland::Client::DataOffer *>();
0390     QVERIFY(dataOffer);
0391     QCOMPARE(dataOffer->offeredMimeTypes().count(), 1);
0392     QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
0393 
0394     // sending a new mimetype to the selection, should be announced in the offer
0395     QSignalSpy mimeTypeAddedSpy(dataOffer, &KWayland::Client::DataOffer::mimeTypeOffered);
0396     dataSource->offer(QStringLiteral("text/html"));
0397     QVERIFY(mimeTypeAddedSpy.wait());
0398     QCOMPARE(mimeTypeAddedSpy.count(), 1);
0399     QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html"));
0400     QCOMPARE(dataOffer->offeredMimeTypes().count(), 2);
0401     QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
0402     QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html"));
0403 
0404     // now clear the selection
0405     dataDevice->clearSelection(1);
0406     QVERIFY(selectionChangedSpy.wait());
0407     QCOMPARE(selectionChangedSpy.count(), 2);
0408     QVERIFY(!deviceInterface->selection());
0409 
0410     // set another selection
0411     dataDevice->setSelection(2, dataSource.get());
0412     QVERIFY(selectionChangedSpy.wait());
0413     // now unbind the dataDevice
0414     QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed);
0415     dataDevice.reset();
0416     QVERIFY(unboundSpy.wait());
0417 }
0418 
0419 void TestDataDevice::testSendSelectionOnSeat()
0420 {
0421     // this test verifies that the selection is sent when setting a focused keyboard
0422     using namespace KWin;
0423     // first add keyboard support to Seat
0424     QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
0425     m_seatInterface->setHasKeyboard(true);
0426     QVERIFY(keyboardChangedSpy.wait());
0427     // now create DataDevice, Keyboard and a Surface
0428     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
0429     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0430     QVERIFY(dataDevice->isValid());
0431     QVERIFY(dataDeviceCreatedSpy.wait());
0432     auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0433     QVERIFY(serverDataDevice);
0434     std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
0435     QVERIFY(keyboard->isValid());
0436     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
0437     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
0438     QVERIFY(surface->isValid());
0439     QVERIFY(surfaceCreatedSpy.wait());
0440 
0441     auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0442     QVERIFY(serverSurface);
0443     m_seatInterface->setFocusedKeyboardSurface(serverSurface);
0444 
0445     // now set the selection
0446     std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
0447     QVERIFY(dataSource->isValid());
0448     dataSource->offer(QStringLiteral("text/plain"));
0449     dataDevice->setSelection(1, dataSource.get());
0450     // we should get a selection offered for that on the data device
0451     QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
0452     QVERIFY(selectionOfferedSpy.wait());
0453     QCOMPARE(selectionOfferedSpy.count(), 1);
0454 
0455     // now unfocus the keyboard
0456     m_seatInterface->setFocusedKeyboardSurface(nullptr);
0457     // if setting the same surface again, we should get another offer
0458     m_seatInterface->setFocusedKeyboardSurface(serverSurface);
0459     QVERIFY(selectionOfferedSpy.wait());
0460     QCOMPARE(selectionOfferedSpy.count(), 2);
0461 
0462     // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd
0463     m_seatInterface->setFocusedKeyboardSurface(nullptr);
0464     QSignalSpy unboundSpy(serverDataDevice, &QObject::destroyed);
0465     dataDevice.reset();
0466     QVERIFY(unboundSpy.wait());
0467     m_seatInterface->setFocusedKeyboardSurface(serverSurface);
0468 }
0469 
0470 void TestDataDevice::testReplaceSource()
0471 {
0472     // this test verifies that replacing a data source cancels the previous source
0473     using namespace KWin;
0474     // first add keyboard support to Seat
0475     QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
0476     m_seatInterface->setHasKeyboard(true);
0477     QVERIFY(keyboardChangedSpy.wait());
0478     // now create DataDevice, Keyboard and a Surface
0479     QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
0480     std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
0481     QVERIFY(dataDevice->isValid());
0482     QVERIFY(dataDeviceCreatedSpy.wait());
0483     auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
0484     QVERIFY(serverDataDevice);
0485     std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
0486     QVERIFY(keyboard->isValid());
0487     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
0488     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
0489     QVERIFY(surface->isValid());
0490     QVERIFY(surfaceCreatedSpy.wait());
0491 
0492     auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0493     QVERIFY(serverSurface);
0494     m_seatInterface->setFocusedKeyboardSurface(serverSurface);
0495 
0496     // now set the selection
0497     std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
0498     QVERIFY(dataSource->isValid());
0499     dataSource->offer(QStringLiteral("text/plain"));
0500     dataDevice->setSelection(1, dataSource.get());
0501     QSignalSpy sourceCancelledSpy(dataSource.get(), &KWayland::Client::DataSource::cancelled);
0502     // we should get a selection offered for that on the data device
0503     QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
0504     QVERIFY(selectionOfferedSpy.wait());
0505     QCOMPARE(selectionOfferedSpy.count(), 1);
0506 
0507     // create a second data source and replace previous one
0508     std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_dataDeviceManager->createDataSource());
0509     QVERIFY(dataSource2->isValid());
0510     dataSource2->offer(QStringLiteral("text/plain"));
0511     QSignalSpy sourceCancelled2Spy(dataSource2.get(), &KWayland::Client::DataSource::cancelled);
0512     dataDevice->setSelection(1, dataSource2.get());
0513     QCOMPARE(selectionOfferedSpy.count(), 1);
0514     QVERIFY(sourceCancelledSpy.wait());
0515     QCOMPARE(selectionOfferedSpy.count(), 2);
0516     QVERIFY(sourceCancelled2Spy.isEmpty());
0517 
0518     // replace the data source with itself, ensure that it did not get cancelled
0519     dataDevice->setSelection(1, dataSource2.get());
0520     QVERIFY(!sourceCancelled2Spy.wait(500));
0521     QCOMPARE(selectionOfferedSpy.count(), 2);
0522     QVERIFY(sourceCancelled2Spy.isEmpty());
0523 
0524     // create a new DataDevice and replace previous one
0525     std::unique_ptr<KWayland::Client::DataDevice> dataDevice2(m_dataDeviceManager->getDataDevice(m_seat));
0526     QVERIFY(dataDevice2->isValid());
0527     std::unique_ptr<KWayland::Client::DataSource> dataSource3(m_dataDeviceManager->createDataSource());
0528     QVERIFY(dataSource3->isValid());
0529     dataSource3->offer(QStringLiteral("text/plain"));
0530     dataDevice2->setSelection(1, dataSource3.get());
0531     QVERIFY(sourceCancelled2Spy.wait());
0532 
0533     // try to crash by first destroying dataSource3 and setting a new DataSource
0534     std::unique_ptr<KWayland::Client::DataSource> dataSource4(m_dataDeviceManager->createDataSource());
0535     QVERIFY(dataSource4->isValid());
0536     dataSource4->offer(QStringLiteral("text/plain"));
0537     dataSource3.reset();
0538     dataDevice2->setSelection(1, dataSource4.get());
0539     QVERIFY(selectionOfferedSpy.wait());
0540 
0541     auto dataOffer = selectionOfferedSpy.last()[0].value<KWayland::Client::DataOffer *>();
0542 
0543     // try to crash by destroying the data source, then requesting data
0544     dataSource4.reset();
0545     int pipeFds[2] = {0, 0};
0546     QVERIFY(pipe(pipeFds) == 0);
0547 
0548     dataOffer->receive(QStringLiteral("text/plain"), pipeFds[1]);
0549     close(pipeFds[1]);
0550 
0551     // spin the event loop, nothing should explode
0552     QTest::qWait(10);
0553 
0554     close(pipeFds[0]);
0555 }
0556 
0557 QTEST_GUILESS_MAIN(TestDataDevice)
0558 #include "test_datadevice.moc"