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

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 // Qt
0009 #include <QSignalSpy>
0010 #include <QTest>
0011 // client
0012 #include "KWayland/Client/compositor.h"
0013 #include "KWayland/Client/connection_thread.h"
0014 #include "KWayland/Client/event_queue.h"
0015 #include "KWayland/Client/output.h"
0016 #include "KWayland/Client/registry.h"
0017 #include "KWayland/Client/seat.h"
0018 #include "KWayland/Client/shm_pool.h"
0019 #include "KWayland/Client/surface.h"
0020 #include "KWayland/Client/xdgshell.h"
0021 // server
0022 #include "wayland/compositor.h"
0023 #include "wayland/display.h"
0024 #include "wayland/output.h"
0025 #include "wayland/seat.h"
0026 #include "wayland/surface.h"
0027 #include "wayland/xdgshell.h"
0028 
0029 #include "../../../tests/fakeoutput.h"
0030 
0031 using namespace KWin;
0032 
0033 Q_DECLARE_METATYPE(Qt::MouseButton)
0034 
0035 static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0");
0036 
0037 class XdgShellTest : public QObject
0038 {
0039     Q_OBJECT
0040 
0041 private Q_SLOTS:
0042     void init();
0043     void cleanup();
0044 
0045     void testCreateSurface();
0046     void testTitle();
0047     void testWindowClass();
0048     void testMaximize();
0049     void testMinimize();
0050     void testFullscreen();
0051     void testShowWindowMenu();
0052     void testMove();
0053     void testResize_data();
0054     void testResize();
0055     void testTransient();
0056     void testPing();
0057     void testClose();
0058     void testConfigureStates_data();
0059     void testConfigureStates();
0060     void testConfigureMultipleAcks();
0061 
0062 private:
0063     XdgShellInterface *m_xdgShellInterface = nullptr;
0064     KWayland::Client::Compositor *m_compositor = nullptr;
0065     KWayland::Client::XdgShell *m_xdgShell = nullptr;
0066     KWin::Display *m_display = nullptr;
0067     CompositorInterface *m_compositorInterface = nullptr;
0068     std::unique_ptr<FakeOutput> m_output1Handle;
0069     OutputInterface *m_output1Interface = nullptr;
0070     std::unique_ptr<FakeOutput> m_output2Handle;
0071     OutputInterface *m_output2Interface = nullptr;
0072     SeatInterface *m_seatInterface = nullptr;
0073     KWayland::Client::ConnectionThread *m_connection = nullptr;
0074     QThread *m_thread = nullptr;
0075     KWayland::Client::EventQueue *m_queue = nullptr;
0076     KWayland::Client::ShmPool *m_shmPool = nullptr;
0077     KWayland::Client::Output *m_output1 = nullptr;
0078     KWayland::Client::Output *m_output2 = nullptr;
0079     KWayland::Client::Seat *m_seat = nullptr;
0080 };
0081 
0082 #define SURFACE                                                                                              \
0083     QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::toplevelCreated);               \
0084     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());                       \
0085     std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.get())); \
0086     QCOMPARE(xdgSurface->size(), QSize());                                                                   \
0087     QVERIFY(xdgSurfaceCreatedSpy.wait());                                                                    \
0088     auto serverXdgToplevel = xdgSurfaceCreatedSpy.first().first().value<XdgToplevelInterface *>();           \
0089     QVERIFY(serverXdgToplevel);
0090 
0091 void XdgShellTest::init()
0092 {
0093     delete m_display;
0094     m_display = new KWin::Display(this);
0095     m_display->addSocketName(s_socketName);
0096     m_display->start();
0097     QVERIFY(m_display->isRunning());
0098     m_display->createShm();
0099     m_output1Handle = std::make_unique<FakeOutput>();
0100     m_output1Handle->setMode(QSize(1024, 768), 60000);
0101     m_output1Interface = new OutputInterface(m_display, m_output1Handle.get(), m_display);
0102     m_output2Handle = std::make_unique<FakeOutput>();
0103     m_output2Handle->setMode(QSize(1024, 768), 60000);
0104     m_output2Interface = new OutputInterface(m_display, m_output2Handle.get(), m_display);
0105     m_seatInterface = new SeatInterface(m_display, m_display);
0106     m_seatInterface->setHasKeyboard(true);
0107     m_seatInterface->setHasPointer(true);
0108     m_seatInterface->setHasTouch(true);
0109     m_compositorInterface = new CompositorInterface(m_display, m_display);
0110     m_xdgShellInterface = new XdgShellInterface(m_display, m_display);
0111 
0112     // setup connection
0113     m_connection = new KWayland::Client::ConnectionThread;
0114     QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
0115     m_connection->setSocketName(s_socketName);
0116 
0117     m_thread = new QThread(this);
0118     m_connection->moveToThread(m_thread);
0119     m_thread->start();
0120 
0121     m_connection->initConnection();
0122     QVERIFY(connectedSpy.wait());
0123 
0124     m_queue = new KWayland::Client::EventQueue(this);
0125     m_queue->setup(m_connection);
0126 
0127     KWayland::Client::Registry registry;
0128     QSignalSpy interfacesAnnouncedSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
0129     QSignalSpy interfaceAnnouncedSpy(&registry, &KWayland::Client::Registry::interfaceAnnounced);
0130     QSignalSpy outputAnnouncedSpy(&registry, &KWayland::Client::Registry::outputAnnounced);
0131 
0132     QSignalSpy xdgShellAnnouncedSpy(&registry, &KWayland::Client::Registry::xdgShellStableAnnounced);
0133     registry.setEventQueue(m_queue);
0134     registry.create(m_connection);
0135     QVERIFY(registry.isValid());
0136     registry.setup();
0137     QVERIFY(interfacesAnnouncedSpy.wait());
0138 
0139     QCOMPARE(outputAnnouncedSpy.count(), 2);
0140     m_output1 = registry.createOutput(outputAnnouncedSpy.first().at(0).value<quint32>(), outputAnnouncedSpy.first().at(1).value<quint32>(), this);
0141     m_output2 = registry.createOutput(outputAnnouncedSpy.last().at(0).value<quint32>(), outputAnnouncedSpy.last().at(1).value<quint32>(), this);
0142 
0143     m_shmPool = registry.createShmPool(registry.interface(KWayland::Client::Registry::Interface::Shm).name, registry.interface(KWayland::Client::Registry::Interface::Shm).version, this);
0144     QVERIFY(m_shmPool);
0145     QVERIFY(m_shmPool->isValid());
0146 
0147     m_compositor =
0148         registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
0149     QVERIFY(m_compositor);
0150     QVERIFY(m_compositor->isValid());
0151 
0152     m_seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this);
0153     QVERIFY(m_seat);
0154     QVERIFY(m_seat->isValid());
0155 
0156     QCOMPARE(xdgShellAnnouncedSpy.count(), 1);
0157 
0158     m_xdgShell = registry.createXdgShell(registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).name,
0159                                          registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).version,
0160                                          this);
0161     QVERIFY(m_xdgShell);
0162     QVERIFY(m_xdgShell->isValid());
0163 }
0164 
0165 void XdgShellTest::cleanup()
0166 {
0167 #define CLEANUP(variable)   \
0168     if (variable) {         \
0169         delete variable;    \
0170         variable = nullptr; \
0171     }
0172     CLEANUP(m_xdgShell)
0173     CLEANUP(m_compositor)
0174     CLEANUP(m_shmPool)
0175     CLEANUP(m_output1)
0176     CLEANUP(m_output2)
0177     CLEANUP(m_seat)
0178     CLEANUP(m_queue)
0179     if (m_connection) {
0180         m_connection->deleteLater();
0181         m_connection = nullptr;
0182     }
0183     if (m_thread) {
0184         m_thread->quit();
0185         m_thread->wait();
0186         delete m_thread;
0187         m_thread = nullptr;
0188     }
0189 
0190     CLEANUP(m_display)
0191 #undef CLEANUP
0192 
0193     // these are the children of the display
0194     m_compositorInterface = nullptr;
0195     m_xdgShellInterface = nullptr;
0196     m_output1Handle.reset();
0197     m_output1Interface = nullptr;
0198     m_output2Handle.reset();
0199     m_output2Interface = nullptr;
0200     m_seatInterface = nullptr;
0201 }
0202 
0203 void XdgShellTest::testCreateSurface()
0204 {
0205     // this test verifies that we can create a surface
0206     // first created the signal spies for the server
0207     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
0208     QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::toplevelCreated);
0209 
0210     // create surface
0211     std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
0212     QVERIFY(surface != nullptr);
0213     QVERIFY(surfaceCreatedSpy.wait());
0214     auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0215     QVERIFY(serverSurface);
0216 
0217     // create shell surface
0218     std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.get()));
0219     QVERIFY(xdgSurface != nullptr);
0220     QVERIFY(xdgSurfaceCreatedSpy.wait());
0221     // verify base things
0222     auto serverToplevel = xdgSurfaceCreatedSpy.first().first().value<XdgToplevelInterface *>();
0223     QVERIFY(serverToplevel);
0224     QCOMPARE(serverToplevel->windowTitle(), QString());
0225     QCOMPARE(serverToplevel->windowClass(), QByteArray());
0226     QCOMPARE(serverToplevel->parentXdgToplevel(), nullptr);
0227     QCOMPARE(serverToplevel->surface(), serverSurface);
0228 
0229     // now let's destroy it
0230     QSignalSpy destroyedSpy(serverToplevel, &QObject::destroyed);
0231     xdgSurface.reset();
0232     QVERIFY(destroyedSpy.wait());
0233 }
0234 
0235 void XdgShellTest::testTitle()
0236 {
0237     // this test verifies that we can change the title of a shell surface
0238     // first create surface
0239     SURFACE
0240 
0241     // should not have a title yet
0242     QCOMPARE(serverXdgToplevel->windowTitle(), QString());
0243 
0244     // lets' change the title
0245     QSignalSpy titleChangedSpy(serverXdgToplevel, &XdgToplevelInterface::windowTitleChanged);
0246     xdgSurface->setTitle(QStringLiteral("foo"));
0247     QVERIFY(titleChangedSpy.wait());
0248     QCOMPARE(titleChangedSpy.count(), 1);
0249     QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("foo"));
0250     QCOMPARE(serverXdgToplevel->windowTitle(), QStringLiteral("foo"));
0251 }
0252 
0253 void XdgShellTest::testWindowClass()
0254 {
0255     // this test verifies that we can change the window class/app id of a shell surface
0256     // first create surface
0257     SURFACE
0258 
0259     // should not have a window class yet
0260     QCOMPARE(serverXdgToplevel->windowClass(), QByteArray());
0261 
0262     // let's change the window class
0263     QSignalSpy windowClassChanged(serverXdgToplevel, &XdgToplevelInterface::windowClassChanged);
0264     xdgSurface->setAppId(QByteArrayLiteral("org.kde.xdgsurfacetest"));
0265     QVERIFY(windowClassChanged.wait());
0266     QCOMPARE(windowClassChanged.count(), 1);
0267     QCOMPARE(windowClassChanged.first().first().toByteArray(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
0268     QCOMPARE(serverXdgToplevel->windowClass(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
0269 }
0270 
0271 void XdgShellTest::testMaximize()
0272 {
0273     // this test verifies that the maximize/unmaximize calls work
0274     SURFACE
0275 
0276     QSignalSpy maximizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::maximizeRequested);
0277     QSignalSpy unmaximizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::unmaximizeRequested);
0278 
0279     xdgSurface->setMaximized(true);
0280     QVERIFY(maximizeRequestedSpy.wait());
0281     QCOMPARE(maximizeRequestedSpy.count(), 1);
0282 
0283     xdgSurface->setMaximized(false);
0284     QVERIFY(unmaximizeRequestedSpy.wait());
0285     QCOMPARE(unmaximizeRequestedSpy.count(), 1);
0286 }
0287 
0288 void XdgShellTest::testMinimize()
0289 {
0290     // this test verifies that the minimize request is delivered
0291     SURFACE
0292 
0293     QSignalSpy minimizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::minimizeRequested);
0294 
0295     xdgSurface->requestMinimize();
0296     QVERIFY(minimizeRequestedSpy.wait());
0297     QCOMPARE(minimizeRequestedSpy.count(), 1);
0298 }
0299 
0300 void XdgShellTest::testFullscreen()
0301 {
0302     qRegisterMetaType<OutputInterface *>();
0303     // this test verifies going to/from fullscreen
0304     SURFACE
0305 
0306     QSignalSpy fullscreenRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::fullscreenRequested);
0307     QSignalSpy unfullscreenRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::unfullscreenRequested);
0308 
0309     // without an output
0310     xdgSurface->setFullscreen(true, nullptr);
0311     QVERIFY(fullscreenRequestedSpy.wait());
0312     QCOMPARE(fullscreenRequestedSpy.count(), 1);
0313     QVERIFY(!fullscreenRequestedSpy.last().at(0).value<OutputInterface *>());
0314 
0315     // unset
0316     xdgSurface->setFullscreen(false);
0317     QVERIFY(unfullscreenRequestedSpy.wait());
0318     QCOMPARE(unfullscreenRequestedSpy.count(), 1);
0319 
0320     // with outputs
0321     xdgSurface->setFullscreen(true, m_output1);
0322     QVERIFY(fullscreenRequestedSpy.wait());
0323     QCOMPARE(fullscreenRequestedSpy.count(), 2);
0324     QCOMPARE(fullscreenRequestedSpy.last().at(0).value<OutputInterface *>(), m_output1Interface);
0325 
0326     // now other output
0327     xdgSurface->setFullscreen(true, m_output2);
0328     QVERIFY(fullscreenRequestedSpy.wait());
0329     QCOMPARE(fullscreenRequestedSpy.count(), 3);
0330     QCOMPARE(fullscreenRequestedSpy.last().at(0).value<OutputInterface *>(), m_output2Interface);
0331 }
0332 
0333 void XdgShellTest::testShowWindowMenu()
0334 {
0335     qRegisterMetaType<SeatInterface *>();
0336     // this test verifies that the show window menu request works
0337     SURFACE
0338 
0339     // hack: pretend that the xdg-surface had been configured
0340     serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
0341 
0342     QSignalSpy windowMenuSpy(serverXdgToplevel, &XdgToplevelInterface::windowMenuRequested);
0343 
0344     // TODO: the serial needs to be a proper one
0345     xdgSurface->requestShowWindowMenu(m_seat, 20, QPoint(30, 40));
0346     QVERIFY(windowMenuSpy.wait());
0347     QCOMPARE(windowMenuSpy.count(), 1);
0348     QCOMPARE(windowMenuSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
0349     QCOMPARE(windowMenuSpy.first().at(1).toPoint(), QPoint(30, 40));
0350     QCOMPARE(windowMenuSpy.first().at(2).value<quint32>(), 20u);
0351 }
0352 
0353 void XdgShellTest::testMove()
0354 {
0355     qRegisterMetaType<SeatInterface *>();
0356     // this test verifies that the move request works
0357     SURFACE
0358 
0359     // hack: pretend that the xdg-surface had been configured
0360     serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
0361 
0362     QSignalSpy moveSpy(serverXdgToplevel, &XdgToplevelInterface::moveRequested);
0363 
0364     // TODO: the serial needs to be a proper one
0365     xdgSurface->requestMove(m_seat, 50);
0366     QVERIFY(moveSpy.wait());
0367     QCOMPARE(moveSpy.count(), 1);
0368     QCOMPARE(moveSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
0369     QCOMPARE(moveSpy.first().at(1).value<quint32>(), 50u);
0370 }
0371 
0372 void XdgShellTest::testResize_data()
0373 {
0374     QTest::addColumn<Qt::Edges>("edges");
0375     QTest::addColumn<XdgToplevelInterface::ResizeAnchor>("anchor");
0376 
0377     QTest::newRow("none") << Qt::Edges() << XdgToplevelInterface::ResizeAnchor::None;
0378     QTest::newRow("top") << Qt::Edges(Qt::TopEdge) << XdgToplevelInterface::ResizeAnchor::Top;
0379     QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge) << XdgToplevelInterface::ResizeAnchor::Bottom;
0380     QTest::newRow("left") << Qt::Edges(Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::Left;
0381     QTest::newRow("top left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::TopLeft;
0382     QTest::newRow("bottom left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::BottomLeft;
0383     QTest::newRow("right") << Qt::Edges(Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::Right;
0384     QTest::newRow("top right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::TopRight;
0385     QTest::newRow("bottom right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::BottomRight;
0386 }
0387 
0388 void XdgShellTest::testResize()
0389 {
0390     qRegisterMetaType<SeatInterface *>();
0391     // this test verifies that the resize request works
0392     SURFACE
0393 
0394     // hack: pretend that the xdg-surface had been configured
0395     serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
0396 
0397     QSignalSpy resizeSpy(serverXdgToplevel, &XdgToplevelInterface::resizeRequested);
0398 
0399     // TODO: the serial needs to be a proper one
0400     QFETCH(Qt::Edges, edges);
0401     xdgSurface->requestResize(m_seat, 60, edges);
0402     QVERIFY(resizeSpy.wait());
0403     QCOMPARE(resizeSpy.count(), 1);
0404     QCOMPARE(resizeSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
0405     QTEST(resizeSpy.first().at(1).value<XdgToplevelInterface::ResizeAnchor>(), "anchor");
0406     QCOMPARE(resizeSpy.first().at(2).value<quint32>(), 60u);
0407 }
0408 
0409 void XdgShellTest::testTransient()
0410 {
0411     // this test verifies that setting the transient for works
0412     SURFACE
0413     std::unique_ptr<KWayland::Client::Surface> surface2(m_compositor->createSurface());
0414     std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface2(m_xdgShell->createSurface(surface2.get()));
0415     QVERIFY(xdgSurfaceCreatedSpy.wait());
0416     auto serverXdgToplevel2 = xdgSurfaceCreatedSpy.last().first().value<XdgToplevelInterface *>();
0417     QVERIFY(serverXdgToplevel2);
0418 
0419     QVERIFY(!serverXdgToplevel->parentXdgToplevel());
0420     QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
0421 
0422     // now make xdsgSurface2 a transient for xdgSurface
0423     QSignalSpy transientForSpy(serverXdgToplevel2, &XdgToplevelInterface::parentXdgToplevelChanged);
0424     xdgSurface2->setTransientFor(xdgSurface.get());
0425 
0426     QVERIFY(transientForSpy.wait());
0427     QCOMPARE(transientForSpy.count(), 1);
0428     QCOMPARE(serverXdgToplevel2->parentXdgToplevel(), serverXdgToplevel);
0429     QVERIFY(!serverXdgToplevel->parentXdgToplevel());
0430 
0431     // unset the transient for
0432     xdgSurface2->setTransientFor(nullptr);
0433     QVERIFY(transientForSpy.wait());
0434     QCOMPARE(transientForSpy.count(), 2);
0435     QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
0436     QVERIFY(!serverXdgToplevel->parentXdgToplevel());
0437 }
0438 
0439 void XdgShellTest::testPing()
0440 {
0441     // this test verifies that a ping request is sent to the client
0442     SURFACE
0443 
0444     QSignalSpy pingSpy(m_xdgShellInterface, &XdgShellInterface::pongReceived);
0445 
0446     quint32 serial = m_xdgShellInterface->ping(serverXdgToplevel->xdgSurface());
0447     QVERIFY(pingSpy.wait());
0448     QCOMPARE(pingSpy.count(), 1);
0449     QCOMPARE(pingSpy.takeFirst().at(0).value<quint32>(), serial);
0450 
0451     // test of a ping failure
0452     // disconnecting the connection thread to the queue will break the connection and pings will do a timeout
0453     disconnect(m_connection, &KWayland::Client::ConnectionThread::eventsRead, m_queue, &KWayland::Client::EventQueue::dispatch);
0454     m_xdgShellInterface->ping(serverXdgToplevel->xdgSurface());
0455     QSignalSpy pingDelayedSpy(m_xdgShellInterface, &XdgShellInterface::pingDelayed);
0456     QVERIFY(pingDelayedSpy.wait());
0457 
0458     QSignalSpy pingTimeoutSpy(m_xdgShellInterface, &XdgShellInterface::pingTimeout);
0459     QVERIFY(pingTimeoutSpy.wait());
0460 }
0461 
0462 void XdgShellTest::testClose()
0463 {
0464     // this test verifies that a close request is sent to the client
0465     SURFACE
0466 
0467     QSignalSpy closeSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::closeRequested);
0468 
0469     serverXdgToplevel->sendClose();
0470     QVERIFY(closeSpy.wait());
0471     QCOMPARE(closeSpy.count(), 1);
0472 
0473     QSignalSpy destroyedSpy(serverXdgToplevel, &XdgToplevelInterface::destroyed);
0474     xdgSurface.reset();
0475     QVERIFY(destroyedSpy.wait());
0476 }
0477 
0478 void XdgShellTest::testConfigureStates_data()
0479 {
0480     QTest::addColumn<XdgToplevelInterface::States>("serverStates");
0481     QTest::addColumn<KWayland::Client::XdgShellSurface::States>("clientStates");
0482 
0483     const auto sa = XdgToplevelInterface::States(XdgToplevelInterface::State::Activated);
0484     const auto sm = XdgToplevelInterface::States(XdgToplevelInterface::State::Maximized);
0485     const auto sf = XdgToplevelInterface::States(XdgToplevelInterface::State::FullScreen);
0486     const auto sr = XdgToplevelInterface::States(XdgToplevelInterface::State::Resizing);
0487 
0488     const auto ca = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Activated);
0489     const auto cm = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Maximized);
0490     const auto cf = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Fullscreen);
0491     const auto cr = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Resizing);
0492 
0493     QTest::newRow("none") << XdgToplevelInterface::States() << KWayland::Client::XdgShellSurface::States();
0494     QTest::newRow("Active") << sa << ca;
0495     QTest::newRow("Maximize") << sm << cm;
0496     QTest::newRow("Fullscreen") << sf << cf;
0497     QTest::newRow("Resizing") << sr << cr;
0498 
0499     QTest::newRow("Active/Maximize") << (sa | sm) << (ca | cm);
0500     QTest::newRow("Active/Fullscreen") << (sa | sf) << (ca | cf);
0501     QTest::newRow("Active/Resizing") << (sa | sr) << (ca | cr);
0502     QTest::newRow("Maximize/Fullscreen") << (sm | sf) << (cm | cf);
0503     QTest::newRow("Maximize/Resizing") << (sm | sr) << (cm | cr);
0504     QTest::newRow("Fullscreen/Resizing") << (sf | sr) << (cf | cr);
0505 
0506     QTest::newRow("Active/Maximize/Fullscreen") << (sa | sm | sf) << (ca | cm | cf);
0507     QTest::newRow("Active/Maximize/Resizing") << (sa | sm | sr) << (ca | cm | cr);
0508     QTest::newRow("Maximize/Fullscreen|Resizing") << (sm | sf | sr) << (cm | cf | cr);
0509 
0510     QTest::newRow("Active/Maximize/Fullscreen/Resizing") << (sa | sm | sf | sr) << (ca | cm | cf | cr);
0511 }
0512 
0513 void XdgShellTest::testConfigureStates()
0514 {
0515     qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
0516     // this test verifies that configure states works
0517     SURFACE
0518 
0519     QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
0520 
0521     QFETCH(XdgToplevelInterface::States, serverStates);
0522     serverXdgToplevel->sendConfigure(QSize(0, 0), serverStates);
0523     QVERIFY(configureSpy.wait());
0524     QCOMPARE(configureSpy.count(), 1);
0525     QCOMPARE(configureSpy.first().at(0).toSize(), QSize(0, 0));
0526     QTEST(configureSpy.first().at(1).value<KWayland::Client::XdgShellSurface::States>(), "clientStates");
0527     QCOMPARE(configureSpy.first().at(2).value<quint32>(), m_display->serial());
0528 
0529     QSignalSpy ackSpy(serverXdgToplevel->xdgSurface(), &XdgSurfaceInterface::configureAcknowledged);
0530 
0531     xdgSurface->ackConfigure(configureSpy.first().at(2).value<quint32>());
0532     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0533     QVERIFY(ackSpy.wait());
0534     QCOMPARE(ackSpy.count(), 1);
0535     QCOMPARE(ackSpy.first().first().value<quint32>(), configureSpy.first().at(2).value<quint32>());
0536 }
0537 
0538 void XdgShellTest::testConfigureMultipleAcks()
0539 {
0540     qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
0541     // this test verifies that with multiple configure requests the last acknowledged one acknowledges all
0542     SURFACE
0543 
0544     QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
0545     QSignalSpy sizeChangedSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::sizeChanged);
0546     QSignalSpy ackSpy(serverXdgToplevel->xdgSurface(), &XdgSurfaceInterface::configureAcknowledged);
0547 
0548     serverXdgToplevel->sendConfigure(QSize(10, 20), XdgToplevelInterface::States());
0549     const quint32 serial1 = m_display->serial();
0550     serverXdgToplevel->sendConfigure(QSize(20, 30), XdgToplevelInterface::States());
0551     const quint32 serial2 = m_display->serial();
0552     QVERIFY(serial1 != serial2);
0553     serverXdgToplevel->sendConfigure(QSize(30, 40), XdgToplevelInterface::States());
0554     const quint32 serial3 = m_display->serial();
0555     QVERIFY(serial1 != serial3);
0556     QVERIFY(serial2 != serial3);
0557 
0558     QVERIFY(configureSpy.wait());
0559     QCOMPARE(configureSpy.count(), 3);
0560     QCOMPARE(configureSpy.at(0).at(0).toSize(), QSize(10, 20));
0561     QCOMPARE(configureSpy.at(0).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
0562     QCOMPARE(configureSpy.at(0).at(2).value<quint32>(), serial1);
0563     QCOMPARE(configureSpy.at(1).at(0).toSize(), QSize(20, 30));
0564     QCOMPARE(configureSpy.at(1).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
0565     QCOMPARE(configureSpy.at(1).at(2).value<quint32>(), serial2);
0566     QCOMPARE(configureSpy.at(2).at(0).toSize(), QSize(30, 40));
0567     QCOMPARE(configureSpy.at(2).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
0568     QCOMPARE(configureSpy.at(2).at(2).value<quint32>(), serial3);
0569     QCOMPARE(sizeChangedSpy.count(), 3);
0570     QCOMPARE(sizeChangedSpy.at(0).at(0).toSize(), QSize(10, 20));
0571     QCOMPARE(sizeChangedSpy.at(1).at(0).toSize(), QSize(20, 30));
0572     QCOMPARE(sizeChangedSpy.at(2).at(0).toSize(), QSize(30, 40));
0573     QCOMPARE(xdgSurface->size(), QSize(30, 40));
0574 
0575     xdgSurface->ackConfigure(serial3);
0576     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0577     QVERIFY(ackSpy.wait());
0578     QCOMPARE(ackSpy.count(), 1);
0579     QCOMPARE(ackSpy.last().first().value<quint32>(), serial3);
0580 
0581     // configure once more with a null size
0582     serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
0583     // should not change size
0584     QVERIFY(configureSpy.wait());
0585     QCOMPARE(configureSpy.count(), 4);
0586     QCOMPARE(sizeChangedSpy.count(), 3);
0587     QCOMPARE(xdgSurface->size(), QSize(30, 40));
0588 }
0589 
0590 QTEST_GUILESS_MAIN(XdgShellTest)
0591 #include "test_xdg_shell.moc"