Changeset View
Changeset View
Standalone View
Standalone View
autotests/client/test_xdg_shell.cpp
Show All 11 Lines | |||||
12 | This library is distributed in the hope that it will be useful, | 12 | This library is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | Lesser General Public License for more details. | 15 | Lesser General Public License for more details. | ||
16 | 16 | | |||
17 | You should have received a copy of the GNU Lesser General Public | 17 | You should have received a copy of the GNU Lesser General Public | ||
18 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | 18 | License along with this library. If not, see <http://www.gnu.org/licenses/>. | ||
19 | *********************************************************************/ | 19 | *********************************************************************/ | ||
20 | // Qt | 20 | | ||
21 | #include <QtTest/QtTest> | 21 | #include "test_xdg_shell.h" | ||
22 | // client | 22 | | ||
23 | #include "../../src/client/xdgshell.h" | 23 | XdgShellTest::XdgShellTest(XdgShellInterfaceVersion version): | ||
24 | #include "../../src/client/connection_thread.h" | 24 | m_version(version) | ||
25 | #include "../../src/client/compositor.h" | 25 | {} | ||
26 | #include "../../src/client/event_queue.h" | | |||
27 | #include "../../src/client/registry.h" | | |||
28 | #include "../../src/client/output.h" | | |||
29 | #include "../../src/client/seat.h" | | |||
30 | #include "../../src/client/shm_pool.h" | | |||
31 | #include "../../src/client/surface.h" | | |||
32 | // server | | |||
33 | #include "../../src/server/display.h" | | |||
34 | #include "../../src/server/compositor_interface.h" | | |||
35 | #include "../../src/server/output_interface.h" | | |||
36 | #include "../../src/server/seat_interface.h" | | |||
37 | #include "../../src/server/surface_interface.h" | | |||
38 | #include "../../src/server/xdgshell_interface.h" | | |||
39 | | ||||
40 | using namespace KWayland::Client; | | |||
41 | using namespace KWayland::Server; | | |||
42 | | ||||
43 | Q_DECLARE_METATYPE(Qt::MouseButton) | | |||
44 | | ||||
45 | class XdgShellTest : public QObject | | |||
46 | { | | |||
47 | Q_OBJECT | | |||
48 | private Q_SLOTS: | | |||
49 | void init(); | | |||
50 | void cleanup(); | | |||
51 | | ||||
52 | void testCreateSurface(); | | |||
53 | void testTitle(); | | |||
54 | void testWindowClass(); | | |||
55 | void testMaximize(); | | |||
56 | void testMinimize(); | | |||
57 | void testFullscreen(); | | |||
58 | void testShowWindowMenu(); | | |||
59 | void testMove(); | | |||
60 | void testResize_data(); | | |||
61 | void testResize(); | | |||
62 | void testTransient(); | | |||
63 | void testClose(); | | |||
64 | void testConfigureStates_data(); | | |||
65 | void testConfigureStates(); | | |||
66 | void testConfigureMultipleAcks(); | | |||
67 | void testPopup(); | | |||
68 | | ||||
69 | private: | | |||
70 | Display *m_display = nullptr; | | |||
71 | CompositorInterface *m_compositorInterface = nullptr; | | |||
72 | OutputInterface *m_o1Interface = nullptr; | | |||
73 | OutputInterface *m_o2Interface = nullptr; | | |||
74 | SeatInterface *m_seatInterface = nullptr; | | |||
75 | XdgShellInterface *m_xdgShellInterface = nullptr; | | |||
76 | ConnectionThread *m_connection = nullptr; | | |||
77 | QThread *m_thread = nullptr; | | |||
78 | EventQueue *m_queue = nullptr; | | |||
79 | Compositor *m_compositor = nullptr; | | |||
80 | ShmPool *m_shmPool = nullptr; | | |||
81 | XdgShell *m_xdgShell = nullptr; | | |||
82 | Output *m_output1 = nullptr; | | |||
83 | Output *m_output2 = nullptr; | | |||
84 | Seat *m_seat = nullptr; | | |||
85 | }; | | |||
86 | 26 | | |||
87 | static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0"); | 27 | static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0"); | ||
88 | 28 | | |||
89 | void XdgShellTest::init() | 29 | void XdgShellTest::init() | ||
90 | { | 30 | { | ||
91 | delete m_display; | 31 | delete m_display; | ||
92 | m_display = new Display(this); | 32 | m_display = new Display(this); | ||
93 | m_display->setSocketName(s_socketName); | 33 | m_display->setSocketName(s_socketName); | ||
94 | m_display->start(); | 34 | m_display->start(); | ||
95 | QVERIFY(m_display->isRunning()); | 35 | QVERIFY(m_display->isRunning()); | ||
96 | m_display->createShm(); | 36 | m_display->createShm(); | ||
97 | m_o1Interface = m_display->createOutput(m_display); | 37 | m_o1Interface = m_display->createOutput(m_display); | ||
98 | m_o1Interface->addMode(QSize(1024, 768)); | 38 | m_o1Interface->addMode(QSize(1024, 768)); | ||
99 | m_o1Interface->create(); | 39 | m_o1Interface->create(); | ||
100 | m_o2Interface = m_display->createOutput(m_display); | 40 | m_o2Interface = m_display->createOutput(m_display); | ||
101 | m_o2Interface->addMode(QSize(1024, 768)); | 41 | m_o2Interface->addMode(QSize(1024, 768)); | ||
102 | m_o2Interface->create(); | 42 | m_o2Interface->create(); | ||
103 | m_seatInterface = m_display->createSeat(m_display); | 43 | m_seatInterface = m_display->createSeat(m_display); | ||
104 | m_seatInterface->setHasKeyboard(true); | 44 | m_seatInterface->setHasKeyboard(true); | ||
105 | m_seatInterface->setHasPointer(true); | 45 | m_seatInterface->setHasPointer(true); | ||
106 | m_seatInterface->setHasTouch(true); | 46 | m_seatInterface->setHasTouch(true); | ||
107 | m_seatInterface->create(); | 47 | m_seatInterface->create(); | ||
108 | m_compositorInterface = m_display->createCompositor(m_display); | 48 | m_compositorInterface = m_display->createCompositor(m_display); | ||
109 | m_compositorInterface->create(); | 49 | m_compositorInterface->create(); | ||
110 | m_xdgShellInterface = m_display->createXdgShell(XdgShellInterfaceVersion::UnstableV5, m_display); | 50 | m_xdgShellInterface = m_display->createXdgShell(m_version, m_display); | ||
111 | QCOMPARE(m_xdgShellInterface->interfaceVersion(), XdgShellInterfaceVersion::UnstableV5); | 51 | QCOMPARE(m_xdgShellInterface->interfaceVersion(), m_version); | ||
112 | m_xdgShellInterface->create(); | 52 | m_xdgShellInterface->create(); | ||
113 | 53 | | |||
114 | // setup connection | 54 | // setup connection | ||
115 | m_connection = new KWayland::Client::ConnectionThread; | 55 | m_connection = new KWayland::Client::ConnectionThread; | ||
116 | QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); | 56 | QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); | ||
117 | QVERIFY(connectedSpy.isValid()); | 57 | QVERIFY(connectedSpy.isValid()); | ||
118 | m_connection->setSocketName(s_socketName); | 58 | m_connection->setSocketName(s_socketName); | ||
119 | 59 | | |||
Show All 9 Lines | |||||
129 | 69 | | |||
130 | Registry registry; | 70 | Registry registry; | ||
131 | QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); | 71 | QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); | ||
132 | QVERIFY(interfacesAnnouncedSpy.isValid()); | 72 | QVERIFY(interfacesAnnouncedSpy.isValid()); | ||
133 | QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced); | 73 | QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced); | ||
134 | QVERIFY(interfaceAnnouncedSpy.isValid()); | 74 | QVERIFY(interfaceAnnouncedSpy.isValid()); | ||
135 | QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); | 75 | QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced); | ||
136 | QVERIFY(outputAnnouncedSpy.isValid()); | 76 | QVERIFY(outputAnnouncedSpy.isValid()); | ||
137 | QSignalSpy xdgShellAnnouncedSpy(®istry, &Registry::xdgShellUnstableV5Announced); | 77 | | ||
78 | auto shellAnnouncedSignal = m_version == XdgShellInterfaceVersion::UnstableV5 ? | ||||
79 | &Registry::xdgShellUnstableV5Announced : &Registry::xdgShellUnstableV6Announced; | ||||
80 | | ||||
81 | QSignalSpy xdgShellAnnouncedSpy(®istry, shellAnnouncedSignal); | ||||
138 | QVERIFY(xdgShellAnnouncedSpy.isValid()); | 82 | QVERIFY(xdgShellAnnouncedSpy.isValid()); | ||
139 | registry.setEventQueue(m_queue); | 83 | registry.setEventQueue(m_queue); | ||
140 | registry.create(m_connection); | 84 | registry.create(m_connection); | ||
141 | QVERIFY(registry.isValid()); | 85 | QVERIFY(registry.isValid()); | ||
142 | registry.setup(); | 86 | registry.setup(); | ||
143 | QVERIFY(interfacesAnnouncedSpy.wait()); | 87 | QVERIFY(interfacesAnnouncedSpy.wait()); | ||
144 | 88 | | |||
145 | QCOMPARE(outputAnnouncedSpy.count(), 2); | 89 | QCOMPARE(outputAnnouncedSpy.count(), 2); | ||
Show All 9 Lines | |||||
155 | QVERIFY(m_compositor->isValid()); | 99 | QVERIFY(m_compositor->isValid()); | ||
156 | 100 | | |||
157 | m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); | 101 | m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this); | ||
158 | QVERIFY(m_seat); | 102 | QVERIFY(m_seat); | ||
159 | QVERIFY(m_seat->isValid()); | 103 | QVERIFY(m_seat->isValid()); | ||
160 | 104 | | |||
161 | QCOMPARE(xdgShellAnnouncedSpy.count(), 1); | 105 | QCOMPARE(xdgShellAnnouncedSpy.count(), 1); | ||
162 | 106 | | |||
163 | m_xdgShell = registry.createXdgShell(registry.interface(Registry::Interface::XdgShellUnstableV5).name, | 107 | Registry::Interface iface = m_version == XdgShellInterfaceVersion::UnstableV5 ? Registry::Interface::XdgShellUnstableV5 : Registry::Interface::XdgShellUnstableV6; | ||
164 | registry.interface(Registry::Interface::XdgShellUnstableV5).version, | 108 | | ||
109 | m_xdgShell = registry.createXdgShell(registry.interface(iface).name, | ||||
110 | registry.interface(iface).version, | ||||
165 | this); | 111 | this); | ||
166 | QVERIFY(m_xdgShell); | 112 | QVERIFY(m_xdgShell); | ||
167 | QVERIFY(m_xdgShell->isValid()); | 113 | QVERIFY(m_xdgShell->isValid()); | ||
168 | } | 114 | } | ||
169 | 115 | | |||
170 | void XdgShellTest::cleanup() | 116 | void XdgShellTest::cleanup() | ||
171 | { | 117 | { | ||
172 | #define CLEANUP(variable) \ | 118 | #define CLEANUP(variable) \ | ||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Line(s) | 151 | { | |||
233 | 179 | | |||
234 | // now let's destroy it | 180 | // now let's destroy it | ||
235 | QSignalSpy destroyedSpy(serverXdgSurface, &QObject::destroyed); | 181 | QSignalSpy destroyedSpy(serverXdgSurface, &QObject::destroyed); | ||
236 | QVERIFY(destroyedSpy.isValid()); | 182 | QVERIFY(destroyedSpy.isValid()); | ||
237 | xdgSurface.reset(); | 183 | xdgSurface.reset(); | ||
238 | QVERIFY(destroyedSpy.wait()); | 184 | QVERIFY(destroyedSpy.wait()); | ||
239 | } | 185 | } | ||
240 | 186 | | |||
241 | #define SURFACE \ | | |||
242 | QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); \ | | |||
243 | QVERIFY(xdgSurfaceCreatedSpy.isValid()); \ | | |||
244 | QScopedPointer<Surface> surface(m_compositor->createSurface()); \ | | |||
245 | QScopedPointer<XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.data())); \ | | |||
246 | QCOMPARE(xdgSurface->size(), QSize()); \ | | |||
247 | QVERIFY(xdgSurfaceCreatedSpy.wait()); \ | | |||
248 | auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value<XdgShellSurfaceInterface*>(); \ | | |||
249 | QVERIFY(serverXdgSurface); | | |||
250 | | ||||
251 | void XdgShellTest::testTitle() | 187 | void XdgShellTest::testTitle() | ||
252 | { | 188 | { | ||
253 | // this test verifies that we can change the title of a shell surface | 189 | // this test verifies that we can change the title of a shell surface | ||
254 | // first create surface | 190 | // first create surface | ||
255 | SURFACE | 191 | SURFACE | ||
256 | 192 | | |||
257 | // should not have a title yet | 193 | // should not have a title yet | ||
258 | QCOMPARE(serverXdgSurface->title(), QString()); | 194 | QCOMPARE(serverXdgSurface->title(), QString()); | ||
▲ Show 20 Lines • Show All 200 Lines • ▼ Show 20 Line(s) | 371 | { | |||
459 | xdgSurface2->setTransientFor(nullptr); | 395 | xdgSurface2->setTransientFor(nullptr); | ||
460 | QVERIFY(transientForSpy.wait()); | 396 | QVERIFY(transientForSpy.wait()); | ||
461 | QCOMPARE(transientForSpy.count(), 2); | 397 | QCOMPARE(transientForSpy.count(), 2); | ||
462 | QVERIFY(!serverXdgSurface2->isTransient()); | 398 | QVERIFY(!serverXdgSurface2->isTransient()); | ||
463 | QVERIFY(serverXdgSurface2->transientFor().isNull()); | 399 | QVERIFY(serverXdgSurface2->transientFor().isNull()); | ||
464 | QVERIFY(!serverXdgSurface->isTransient()); | 400 | QVERIFY(!serverXdgSurface->isTransient()); | ||
465 | } | 401 | } | ||
466 | 402 | | |||
403 | void XdgShellTest::testPing() | ||||
404 | { | ||||
405 | // this test verifies that a ping request is sent to the client | ||||
406 | SURFACE | ||||
407 | | ||||
408 | QSignalSpy pingSpy(m_xdgShellInterface, &XdgShellInterface::pongReceived); | ||||
409 | QVERIFY(pingSpy.isValid()); | ||||
410 | | ||||
411 | quint32 serial = m_xdgShellInterface->ping(); | ||||
412 | QVERIFY(pingSpy.wait()); | ||||
413 | QCOMPARE(pingSpy.count(), 1); | ||||
414 | QCOMPARE(pingSpy.takeFirst().at(0).value<quint32>(), serial); | ||||
415 | | ||||
416 | // test of a ping failure | ||||
417 | // disconnecting the connection thread to the queue will break the connection and pings will do a timeout | ||||
418 | disconnect(m_connection, &ConnectionThread::eventsRead, m_queue, &EventQueue::dispatch); | ||||
419 | m_xdgShellInterface->ping(); | ||||
420 | QSignalSpy pingDelayedSpy(m_xdgShellInterface, &XdgShellInterface::pingDelayed); | ||||
421 | QVERIFY(pingDelayedSpy.wait()); | ||||
422 | | ||||
423 | QSignalSpy pingTimeoutSpy(m_xdgShellInterface, &XdgShellInterface::pingTimeout); | ||||
424 | QVERIFY(pingTimeoutSpy.wait()); | ||||
425 | } | ||||
426 | | ||||
467 | void XdgShellTest::testClose() | 427 | void XdgShellTest::testClose() | ||
468 | { | 428 | { | ||
469 | // this test verifies that a close request is sent to the client | 429 | // this test verifies that a close request is sent to the client | ||
470 | SURFACE | 430 | SURFACE | ||
471 | 431 | | |||
472 | QSignalSpy closeSpy(xdgSurface.data(), &XdgShellSurface::closeRequested); | 432 | QSignalSpy closeSpy(xdgSurface.data(), &XdgShellSurface::closeRequested); | ||
473 | QVERIFY(closeSpy.isValid()); | 433 | QVERIFY(closeSpy.isValid()); | ||
474 | 434 | | |||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Line(s) | 507 | { | |||
594 | serverXdgSurface->configure(XdgShellSurfaceInterface::States()); | 554 | serverXdgSurface->configure(XdgShellSurfaceInterface::States()); | ||
595 | // should not change size | 555 | // should not change size | ||
596 | QVERIFY(configureSpy.wait()); | 556 | QVERIFY(configureSpy.wait()); | ||
597 | QCOMPARE(configureSpy.count(), 4); | 557 | QCOMPARE(configureSpy.count(), 4); | ||
598 | QCOMPARE(sizeChangedSpy.count(), 3); | 558 | QCOMPARE(sizeChangedSpy.count(), 3); | ||
599 | QCOMPARE(xdgSurface->size(), QSize(30, 40)); | 559 | QCOMPARE(xdgSurface->size(), QSize(30, 40)); | ||
600 | } | 560 | } | ||
601 | 561 | | |||
602 | void XdgShellTest::testPopup() | | |||
603 | { | | |||
604 | // this test verifies that the creation of popups works correctly | | |||
605 | SURFACE | | |||
606 | QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); | | |||
607 | QVERIFY(surfaceCreatedSpy.isValid()); | | |||
608 | QSignalSpy xdgPopupSpy(m_xdgShellInterface, &XdgShellInterface::popupCreated); | | |||
609 | QVERIFY(xdgPopupSpy.isValid()); | | |||
610 | | ||||
611 | QScopedPointer<Surface> popupSurface(m_compositor->createSurface()); | | |||
612 | QVERIFY(surfaceCreatedSpy.wait()); | | |||
613 | | ||||
614 | // TODO: proper serial | | |||
615 | QScopedPointer<XdgShellPopup> xdgPopup(m_xdgShell->createPopup(popupSurface.data(), surface.data(), m_seat, 120, QPoint(10, 20))); | | |||
616 | QVERIFY(xdgPopupSpy.wait()); | | |||
617 | QCOMPARE(xdgPopupSpy.count(), 1); | | |||
618 | QCOMPARE(xdgPopupSpy.first().at(1).value<SeatInterface*>(), m_seatInterface); | | |||
619 | QCOMPARE(xdgPopupSpy.first().at(2).value<quint32>(), 120u); | | |||
620 | auto serverXdgPopup = xdgPopupSpy.first().first().value<XdgShellPopupInterface*>(); | | |||
621 | QVERIFY(serverXdgPopup); | | |||
622 | | ||||
623 | QCOMPARE(serverXdgPopup->surface(), surfaceCreatedSpy.first().first().value<SurfaceInterface*>()); | | |||
624 | QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgSurface->surface()); | | |||
625 | QCOMPARE(serverXdgPopup->transientOffset(), QPoint(10, 20)); | | |||
626 | | ||||
627 | // now also a popup for the popup | | |||
628 | QScopedPointer<Surface> popup2Surface(m_compositor->createSurface()); | | |||
629 | QScopedPointer<XdgShellPopup> xdgPopup2(m_xdgShell->createPopup(popup2Surface.data(), popupSurface.data(), m_seat, 121, QPoint(5, 7))); | | |||
630 | QVERIFY(xdgPopupSpy.wait()); | | |||
631 | QCOMPARE(xdgPopupSpy.count(), 2); | | |||
632 | QCOMPARE(xdgPopupSpy.last().at(1).value<SeatInterface*>(), m_seatInterface); | | |||
633 | QCOMPARE(xdgPopupSpy.last().at(2).value<quint32>(), 121u); | | |||
634 | auto serverXdgPopup2 = xdgPopupSpy.last().first().value<XdgShellPopupInterface*>(); | | |||
635 | QVERIFY(serverXdgPopup2); | | |||
636 | | ||||
637 | QCOMPARE(serverXdgPopup2->surface(), surfaceCreatedSpy.last().first().value<SurfaceInterface*>()); | | |||
638 | QCOMPARE(serverXdgPopup2->transientFor().data(), serverXdgPopup->surface()); | | |||
639 | QCOMPARE(serverXdgPopup2->transientOffset(), QPoint(5, 7)); | | |||
640 | | ||||
641 | QSignalSpy popup2DoneSpy(xdgPopup2.data(), &XdgShellPopup::popupDone); | | |||
642 | QVERIFY(popup2DoneSpy.isValid()); | | |||
643 | serverXdgPopup2->popupDone(); | | |||
644 | QVERIFY(popup2DoneSpy.wait()); | | |||
645 | // TODO: test that this sends also the done to all parents | | |||
646 | } | | |||
647 | | ||||
648 | QTEST_GUILESS_MAIN(XdgShellTest) | | |||
649 | #include "test_xdg_shell.moc" | 562 | #include "test_xdg_shell.moc" |