1 /*
2     SPDX-FileCopyrightText: 2016 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 <QSignalSpy>
8 #include <QTest>
9 // client
10 #include "../../src/client/compositor.h"
11 #include "../../src/client/connection_thread.h"
12 #include "../../src/client/event_queue.h"
13 #include "../../src/client/registry.h"
14 #include "../../src/client/shadow.h"
15 #include "../../src/client/shm_pool.h"
16 #include "../../src/client/surface.h"
17 // server
18 #include "../../src/server/buffer_interface.h"
19 #include "../../src/server/compositor_interface.h"
20 #include "../../src/server/display.h"
21 #include "../../src/server/shadow_interface.h"
22 
23 using namespace KWayland::Client;
24 using namespace KWayland::Server;
25 
26 class ShadowTest : public QObject
27 {
28     Q_OBJECT
29 private Q_SLOTS:
30     void init();
31     void cleanup();
32 
33     void testCreateShadow();
34     void testShadowElements();
35     void testSurfaceDestroy();
36 
37 private:
38     Display *m_display = nullptr;
39 
40     ConnectionThread *m_connection = nullptr;
41     CompositorInterface *m_compositorInterface = nullptr;
42     ShadowManagerInterface *m_shadowInterface = nullptr;
43     QThread *m_thread = nullptr;
44     EventQueue *m_queue = nullptr;
45     ShmPool *m_shm = nullptr;
46     Compositor *m_compositor = nullptr;
47     ShadowManager *m_shadow = nullptr;
48 };
49 
50 static const QString s_socketName = QStringLiteral("kwayland-test-shadow-0");
51 
init()52 void ShadowTest::init()
53 {
54     delete m_display;
55     m_display = new Display(this);
56     m_display->setSocketName(s_socketName);
57     m_display->start();
58     QVERIFY(m_display->isRunning());
59     m_display->createShm();
60     m_compositorInterface = m_display->createCompositor(m_display);
61     m_compositorInterface->create();
62     m_shadowInterface = m_display->createShadowManager(m_display);
63     m_shadowInterface->create();
64 
65     // setup connection
66     m_connection = new KWayland::Client::ConnectionThread;
67     QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
68     QVERIFY(connectedSpy.isValid());
69     m_connection->setSocketName(s_socketName);
70 
71     m_thread = new QThread(this);
72     m_connection->moveToThread(m_thread);
73     m_thread->start();
74 
75     m_connection->initConnection();
76     QVERIFY(connectedSpy.wait());
77 
78     m_queue = new EventQueue(this);
79     m_queue->setup(m_connection);
80 
81     Registry registry;
82     QSignalSpy interfacesAnnouncedSpy(&registry, &Registry::interfacesAnnounced);
83     QVERIFY(interfacesAnnouncedSpy.isValid());
84     registry.setEventQueue(m_queue);
85     registry.create(m_connection);
86     QVERIFY(registry.isValid());
87     registry.setup();
88     QVERIFY(interfacesAnnouncedSpy.wait());
89 
90     m_shm = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version, this);
91     QVERIFY(m_shm->isValid());
92     auto compositorInterface = registry.interface(Registry::Interface::Compositor);
93     m_compositor = registry.createCompositor(compositorInterface.name, compositorInterface.version, this);
94     QVERIFY(m_compositor->isValid());
95     m_shadow = registry.createShadowManager(registry.interface(Registry::Interface::Shadow).name, //
96                                             registry.interface(Registry::Interface::Shadow).version,
97                                             this);
98     QVERIFY(m_shadow->isValid());
99 }
100 
cleanup()101 void ShadowTest::cleanup()
102 {
103 #define CLEANUP(variable)                                                                                                                                      \
104     if (variable) {                                                                                                                                            \
105         delete variable;                                                                                                                                       \
106         variable = nullptr;                                                                                                                                    \
107     }
108     CLEANUP(m_shm)
109     CLEANUP(m_compositor)
110     CLEANUP(m_shadow)
111     CLEANUP(m_queue)
112     if (m_connection) {
113         m_connection->deleteLater();
114         m_connection = nullptr;
115     }
116     if (m_thread) {
117         m_thread->quit();
118         m_thread->wait();
119         delete m_thread;
120         m_thread = nullptr;
121     }
122 
123     CLEANUP(m_compositorInterface)
124     CLEANUP(m_shadowInterface)
125     CLEANUP(m_display)
126 #undef CLEANUP
127 }
128 
testCreateShadow()129 void ShadowTest::testCreateShadow()
130 {
131     // this test verifies the basic shadow behavior, create for surface, commit it, etc.
132     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
133     QVERIFY(surfaceCreatedSpy.isValid());
134     QScopedPointer<Surface> surface(m_compositor->createSurface());
135     QVERIFY(surfaceCreatedSpy.wait());
136     auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
137     QVERIFY(serverSurface);
138     // a surface without anything should not have a Shadow
139     QVERIFY(!serverSurface->shadow());
140     QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged);
141     QVERIFY(shadowChangedSpy.isValid());
142 
143     // let's create a shadow for the Surface
144     QScopedPointer<Shadow> shadow(m_shadow->createShadow(surface.data()));
145     // that should not have triggered the shadowChangedSpy)
146     QVERIFY(!shadowChangedSpy.wait(100));
147 
148     // now let's commit the surface, that should trigger the shadow changed
149     surface->commit(Surface::CommitFlag::None);
150     QVERIFY(shadowChangedSpy.wait());
151     QCOMPARE(shadowChangedSpy.count(), 1);
152 
153     // we didn't set anything on the shadow, so it should be all default values
154     auto serverShadow = serverSurface->shadow();
155     QVERIFY(serverShadow);
156     QCOMPARE(serverShadow->offset(), QMarginsF());
157     QVERIFY(!serverShadow->topLeft());
158     QVERIFY(!serverShadow->top());
159     QVERIFY(!serverShadow->topRight());
160     QVERIFY(!serverShadow->right());
161     QVERIFY(!serverShadow->bottomRight());
162     QVERIFY(!serverShadow->bottom());
163     QVERIFY(!serverShadow->bottomLeft());
164     QVERIFY(!serverShadow->left());
165 
166     // now let's remove the shadow
167     m_shadow->removeShadow(surface.data());
168     // just removing should not remove it yet, surface needs to be committed
169     QVERIFY(!shadowChangedSpy.wait(100));
170     surface->commit(Surface::CommitFlag::None);
171     QVERIFY(shadowChangedSpy.wait());
172     QCOMPARE(shadowChangedSpy.count(), 2);
173     QVERIFY(!serverSurface->shadow());
174 }
175 
testShadowElements()176 void ShadowTest::testShadowElements()
177 {
178     // this test verifies that all shadow elements are correctly passed to the server
179     // first create surface
180     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
181     QVERIFY(surfaceCreatedSpy.isValid());
182     QScopedPointer<Surface> surface(m_compositor->createSurface());
183     QVERIFY(surfaceCreatedSpy.wait());
184     auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
185     QVERIFY(serverSurface);
186     QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged);
187     QVERIFY(shadowChangedSpy.isValid());
188 
189     // now create the shadow
190     QScopedPointer<Shadow> shadow(m_shadow->createShadow(surface.data()));
191     QImage topLeftImage(QSize(10, 10), QImage::Format_ARGB32_Premultiplied);
192     topLeftImage.fill(Qt::white);
193     shadow->attachTopLeft(m_shm->createBuffer(topLeftImage));
194     QImage topImage(QSize(11, 11), QImage::Format_ARGB32_Premultiplied);
195     topImage.fill(Qt::black);
196     shadow->attachTop(m_shm->createBuffer(topImage));
197     QImage topRightImage(QSize(12, 12), QImage::Format_ARGB32_Premultiplied);
198     topRightImage.fill(Qt::red);
199     shadow->attachTopRight(m_shm->createBuffer(topRightImage));
200     QImage rightImage(QSize(13, 13), QImage::Format_ARGB32_Premultiplied);
201     rightImage.fill(Qt::darkRed);
202     shadow->attachRight(m_shm->createBuffer(rightImage));
203     QImage bottomRightImage(QSize(14, 14), QImage::Format_ARGB32_Premultiplied);
204     bottomRightImage.fill(Qt::green);
205     shadow->attachBottomRight(m_shm->createBuffer(bottomRightImage));
206     QImage bottomImage(QSize(15, 15), QImage::Format_ARGB32_Premultiplied);
207     bottomImage.fill(Qt::darkGreen);
208     shadow->attachBottom(m_shm->createBuffer(bottomImage));
209     QImage bottomLeftImage(QSize(16, 16), QImage::Format_ARGB32_Premultiplied);
210     bottomLeftImage.fill(Qt::blue);
211     shadow->attachBottomLeft(m_shm->createBuffer(bottomLeftImage));
212     QImage leftImage(QSize(17, 17), QImage::Format_ARGB32_Premultiplied);
213     leftImage.fill(Qt::darkBlue);
214     shadow->attachLeft(m_shm->createBuffer(leftImage));
215     shadow->setOffsets(QMarginsF(1, 2, 3, 4));
216     shadow->commit();
217     surface->commit(Surface::CommitFlag::None);
218 
219     QVERIFY(shadowChangedSpy.wait());
220     auto serverShadow = serverSurface->shadow();
221     QVERIFY(serverShadow);
222     QCOMPARE(serverShadow->offset(), QMarginsF(1, 2, 3, 4));
223     QCOMPARE(serverShadow->topLeft()->data(), topLeftImage);
224     QCOMPARE(serverShadow->top()->data(), topImage);
225     QCOMPARE(serverShadow->topRight()->data(), topRightImage);
226     QCOMPARE(serverShadow->right()->data(), rightImage);
227     QCOMPARE(serverShadow->bottomRight()->data(), bottomRightImage);
228     QCOMPARE(serverShadow->bottom()->data(), bottomImage);
229     QCOMPARE(serverShadow->bottomLeft()->data(), bottomLeftImage);
230     QCOMPARE(serverShadow->left()->data(), leftImage);
231 
232     // try to destroy the buffer
233     // first attach one buffer
234     shadow->attachTopLeft(m_shm->createBuffer(topLeftImage));
235     // create a destroyed signal
236     QSignalSpy destroyedSpy(serverShadow->topLeft(), &BufferInterface::aboutToBeDestroyed);
237     QVERIFY(destroyedSpy.isValid());
238     delete m_shm;
239     m_shm = nullptr;
240     QVERIFY(destroyedSpy.wait());
241 
242     // now all buffers should be gone
243     // TODO: does that need a signal?
244     QVERIFY(!serverShadow->topLeft());
245     QVERIFY(!serverShadow->top());
246     QVERIFY(!serverShadow->topRight());
247     QVERIFY(!serverShadow->right());
248     QVERIFY(!serverShadow->bottomRight());
249     QVERIFY(!serverShadow->bottom());
250     QVERIFY(!serverShadow->bottomLeft());
251     QVERIFY(!serverShadow->left());
252 }
253 
testSurfaceDestroy()254 void ShadowTest::testSurfaceDestroy()
255 {
256     using namespace KWayland::Client;
257     using namespace KWayland::Server;
258     QSignalSpy serverSurfaceCreated(m_compositorInterface, &CompositorInterface::surfaceCreated);
259     QVERIFY(serverSurfaceCreated.isValid());
260 
261     QScopedPointer<KWayland::Client::Surface> surface(m_compositor->createSurface());
262     QVERIFY(serverSurfaceCreated.wait());
263     auto serverSurface = serverSurfaceCreated.first().first().value<SurfaceInterface *>();
264     QSignalSpy shadowChangedSpy(serverSurface, &SurfaceInterface::shadowChanged);
265     QVERIFY(shadowChangedSpy.isValid());
266 
267     QScopedPointer<Shadow> shadow(m_shadow->createShadow(surface.data()));
268     shadow->commit();
269     surface->commit(Surface::CommitFlag::None);
270     QVERIFY(shadowChangedSpy.wait());
271     auto serverShadow = serverSurface->shadow();
272     QVERIFY(serverShadow);
273 
274     // destroy the parent surface
275     QSignalSpy surfaceDestroyedSpy(serverSurface, &QObject::destroyed);
276     QVERIFY(surfaceDestroyedSpy.isValid());
277     QSignalSpy shadowDestroyedSpy(serverShadow.data(), &QObject::destroyed);
278     QVERIFY(shadowDestroyedSpy.isValid());
279     surface.reset();
280     QVERIFY(surfaceDestroyedSpy.wait());
281     QVERIFY(shadowDestroyedSpy.isEmpty());
282     // destroy the shadow
283     shadow.reset();
284     QVERIFY(shadowDestroyedSpy.wait());
285     QCOMPARE(shadowDestroyedSpy.count(), 1);
286 }
287 
288 QTEST_GUILESS_MAIN(ShadowTest)
289 #include "test_shadow.moc"
290