1 /********************************************************************
2 Copyright © 2016 Martin Gräßlin <mgraesslin@kde.org>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
21 #include "../../src/client/connection_thread.h"
22 #include "../../src/client/event_queue.h"
23 #include "../../src/client/plasmawindowmanagement.h"
24 #include "../../src/client/plasmawindowmodel.h"
25 #include "../../src/client/registry.h"
26
27 #include "../../server/display.h"
28 #include "../../server/plasma_virtual_desktop.h"
29 #include "../../server/plasma_window.h"
30
31 #include <QtTest>
32
33 #include <linux/input.h>
34
35 namespace Clt = Wrapland::Client;
36 namespace Srv = Wrapland::Server;
37
38 Q_DECLARE_METATYPE(Qt::MouseButton)
39
40 typedef void (Clt::PlasmaWindow::*ClientWindowSignal)();
41 Q_DECLARE_METATYPE(ClientWindowSignal)
42
43 typedef void (Srv::PlasmaWindow::*ServerWindowBoolSetter)(bool);
44 Q_DECLARE_METATYPE(ServerWindowBoolSetter)
45
46 typedef void (Srv::PlasmaWindow::*ServerWindowStringSetter)(const QString&);
47 Q_DECLARE_METATYPE(ServerWindowStringSetter)
48
49 typedef void (Srv::PlasmaWindow::*ServerWindowQuint32Setter)(quint32);
50 Q_DECLARE_METATYPE(ServerWindowQuint32Setter)
51
52 typedef void (Srv::PlasmaWindow::*ServerWindowVoidSetter)();
53 Q_DECLARE_METATYPE(ServerWindowVoidSetter)
54
55 typedef void (Srv::PlasmaWindow::*ServerWindowIconSetter)(const QIcon&);
56 Q_DECLARE_METATYPE(ServerWindowIconSetter)
57
58 class PlasmaWindowModelTest : public QObject
59 {
60 Q_OBJECT
61 private Q_SLOTS:
62 void init();
63 void cleanup();
64
65 void testRoleNames_data();
66 void testRoleNames();
67
68 void testAddRemoveRows();
69
70 void testDefaultData_data();
71 void testDefaultData();
72
73 void testIsActive();
74 void testIsFullscreenable();
75 void testIsFullscreen();
76 void testIsMaximizable();
77 void testIsMaximized();
78 void testIsMinimizable();
79 void testIsMinimized();
80 void testIsKeepAbove();
81 void testIsKeepBelow();
82
83 void testIsDemandingAttention();
84 void testSkipTaskbar();
85 void testSkipSwitcher();
86
87 void testIsShadeable();
88 void testIsShaded();
89 void testIsMovable();
90 void testIsResizable();
91 void testIsVirtualDesktopChangeable();
92 void testIsCloseable();
93
94 void testGeometry();
95 void testTitle();
96 void testAppId();
97 void testPid();
98 void testVirtualDesktops();
99
100 // TODO icon: can we ensure a theme is installed on CI?
101 void testRequests();
102
103 // TODO: minimized geometry
104 // TODO: model reset
105 void testCreateWithUnmappedWindow();
106 void testChangeWindowAfterModelDestroy_data();
107 void testChangeWindowAfterModelDestroy();
108 void testCreateWindowAfterModelDestroy();
109
110 private:
111 bool testBooleanData(Clt::PlasmaWindowModel::AdditionalRoles role,
112 void (Srv::PlasmaWindow::*function)(bool));
113
114 Srv::Display* m_display = nullptr;
115
116 Srv::PlasmaWindowManager* m_serverWindowManager = nullptr;
117 Clt::PlasmaWindowManagement* m_pw = nullptr;
118
119 Srv::PlasmaVirtualDesktopManager* m_serverPlasmaVirtualDesktopManager = nullptr;
120
121 Clt::ConnectionThread* m_connection = nullptr;
122 QThread* m_thread = nullptr;
123 Clt::EventQueue* m_queue = nullptr;
124 };
125
126 static const QString s_socketName = QStringLiteral("wrapland-test-fake-input-0");
127
init()128 void PlasmaWindowModelTest::init()
129 {
130 delete m_display;
131 m_display = new Srv::Display(this);
132 m_display->setSocketName(s_socketName);
133 m_display->start();
134 QVERIFY(m_display->running());
135
136 m_display->createShm();
137 m_serverWindowManager = m_display->createPlasmaWindowManager();
138
139 m_serverPlasmaVirtualDesktopManager = m_display->createPlasmaVirtualDesktopManager(m_display);
140
141 m_serverPlasmaVirtualDesktopManager->createDesktop("desktop1");
142 m_serverPlasmaVirtualDesktopManager->createDesktop("desktop2");
143 m_serverWindowManager->setVirtualDesktopManager(m_serverPlasmaVirtualDesktopManager);
144
145 // Setup connection.
146 m_connection = new Clt::ConnectionThread;
147 QSignalSpy connectedSpy(m_connection, &Clt::ConnectionThread::establishedChanged);
148 QVERIFY(connectedSpy.isValid());
149 m_connection->setSocketName(s_socketName);
150
151 m_thread = new QThread(this);
152 m_connection->moveToThread(m_thread);
153 m_thread->start();
154
155 m_connection->establishConnection();
156 QVERIFY(connectedSpy.count() || connectedSpy.wait());
157 QCOMPARE(connectedSpy.count(), 1);
158
159 m_queue = new Clt::EventQueue(this);
160 m_queue->setup(m_connection);
161
162 Clt::Registry registry;
163 QSignalSpy interfacesAnnouncedSpy(®istry, &Clt::Registry::interfacesAnnounced);
164 QVERIFY(interfacesAnnouncedSpy.isValid());
165
166 registry.setEventQueue(m_queue);
167 registry.create(m_connection);
168 QVERIFY(registry.isValid());
169
170 registry.setup();
171 QVERIFY(interfacesAnnouncedSpy.wait());
172
173 m_pw = registry.createPlasmaWindowManagement(
174 registry.interface(Clt::Registry::Interface::PlasmaWindowManagement).name,
175 registry.interface(Clt::Registry::Interface::PlasmaWindowManagement).version,
176 this);
177 QVERIFY(m_pw->isValid());
178 }
179
cleanup()180 void PlasmaWindowModelTest::cleanup()
181 {
182 #define CLEANUP(variable) \
183 if (variable) { \
184 delete variable; \
185 variable = nullptr; \
186 }
187 CLEANUP(m_pw)
188 CLEANUP(m_serverPlasmaVirtualDesktopManager)
189 CLEANUP(m_queue)
190 if (m_connection) {
191 m_connection->deleteLater();
192 m_connection = nullptr;
193 }
194 if (m_thread) {
195 m_thread->quit();
196 m_thread->wait();
197 delete m_thread;
198 m_thread = nullptr;
199 }
200
201 CLEANUP(m_serverWindowManager)
202 CLEANUP(m_display)
203 #undef CLEANUP
204 }
205
testBooleanData(Clt::PlasmaWindowModel::AdditionalRoles role,void (Srv::PlasmaWindow::* function)(bool))206 bool PlasmaWindowModelTest::testBooleanData(Clt::PlasmaWindowModel::AdditionalRoles role,
207 void (Srv::PlasmaWindow::*function)(bool))
208 {
209 #define VERIFY(statement) \
210 if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__)) \
211 return false;
212 #define COMPARE(actual, expected) \
213 if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__)) \
214 return false;
215
216 auto* model = m_pw->createWindowModel();
217 VERIFY(model);
218
219 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
220 VERIFY(rowInsertedSpy.isValid());
221
222 QSignalSpy initSpy(m_pw, &Clt::PlasmaWindowManagement::windowCreated);
223 VERIFY(initSpy.isValid());
224 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
225 VERIFY(dataChangedSpy.isValid());
226
227 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
228 VERIFY(serverWindow);
229
230 COMPARE(dataChangedSpy.count(), 0);
231 COMPARE(initSpy.count(), 0);
232
233 VERIFY(rowInsertedSpy.wait());
234 COMPARE(initSpy.count(), 1);
235
236 // Wait for the first event announcing the icon on resource creation.
237 VERIFY(dataChangedSpy.count() == 1 || dataChangedSpy.wait());
238 COMPARE(dataChangedSpy.count(), 1);
239 COMPARE(initSpy.count(), 1);
240
241 // The current API is not very well defined in regards to which evente we can except when.
242 // Make sure we do not get additional data changed signals before actually setting the data.
243 VERIFY(!dataChangedSpy.wait(100));
244
245 const QModelIndex index = model->index(0);
246 COMPARE(model->data(index, role).toBool(), false);
247
248 (serverWindow->*(function))(true);
249
250 VERIFY(dataChangedSpy.wait());
251 COMPARE(dataChangedSpy.count(), 2);
252
253 // Check that there is only one data changed signal we receive.
254 VERIFY(!dataChangedSpy.wait(100));
255
256 COMPARE(dataChangedSpy.last().first().toModelIndex(), index);
257 COMPARE(dataChangedSpy.last().last().value<QVector<int>>(), QVector<int>{int(role)});
258 COMPARE(model->data(index, role).toBool(), true);
259
260 (serverWindow->*(function))(false);
261 VERIFY(dataChangedSpy.wait());
262 COMPARE(dataChangedSpy.count(), 3);
263
264 COMPARE(dataChangedSpy.last().first().toModelIndex(), index);
265 COMPARE(dataChangedSpy.last().last().value<QVector<int>>(), QVector<int>{int(role)});
266 COMPARE(model->data(index, role).toBool(), false);
267
268 #undef COMPARE
269 #undef VERIFY
270 return true;
271 }
272
testRoleNames_data()273 void PlasmaWindowModelTest::testRoleNames_data()
274 {
275 QTest::addColumn<int>("role");
276 QTest::addColumn<QByteArray>("name");
277
278 QTest::newRow("display") << int(Qt::DisplayRole) << QByteArrayLiteral("DisplayRole");
279 QTest::newRow("decoration") << int(Qt::DecorationRole) << QByteArrayLiteral("DecorationRole");
280
281 QTest::newRow("AppId") << int(Clt::PlasmaWindowModel::AppId) << QByteArrayLiteral("AppId");
282 QTest::newRow("Pid") << int(Clt::PlasmaWindowModel::Pid) << QByteArrayLiteral("Pid");
283 QTest::newRow("IsActive") << int(Clt::PlasmaWindowModel::IsActive)
284 << QByteArrayLiteral("IsActive");
285 QTest::newRow("IsFullscreenable")
286 << int(Clt::PlasmaWindowModel::IsFullscreenable) << QByteArrayLiteral("IsFullscreenable");
287 QTest::newRow("IsFullscreen") << int(Clt::PlasmaWindowModel::IsFullscreen)
288 << QByteArrayLiteral("IsFullscreen");
289 QTest::newRow("IsMaximizable")
290 << int(Clt::PlasmaWindowModel::IsMaximizable) << QByteArrayLiteral("IsMaximizable");
291 QTest::newRow("IsMaximized") << int(Clt::PlasmaWindowModel::IsMaximized)
292 << QByteArrayLiteral("IsMaximized");
293 QTest::newRow("IsMinimizable")
294 << int(Clt::PlasmaWindowModel::IsMinimizable) << QByteArrayLiteral("IsMinimizable");
295 QTest::newRow("IsMinimized") << int(Clt::PlasmaWindowModel::IsMinimized)
296 << QByteArrayLiteral("IsMinimized");
297 QTest::newRow("IsKeepAbove") << int(Clt::PlasmaWindowModel::IsKeepAbove)
298 << QByteArrayLiteral("IsKeepAbove");
299 QTest::newRow("IsKeepBelow") << int(Clt::PlasmaWindowModel::IsKeepBelow)
300 << QByteArrayLiteral("IsKeepBelow");
301 QTest::newRow("IsOnAllDesktops")
302 << int(Clt::PlasmaWindowModel::IsOnAllDesktops) << QByteArrayLiteral("IsOnAllDesktops");
303 QTest::newRow("IsDemandingAttention") << int(Clt::PlasmaWindowModel::IsDemandingAttention)
304 << QByteArrayLiteral("IsDemandingAttention");
305 QTest::newRow("SkipTaskbar") << int(Clt::PlasmaWindowModel::SkipTaskbar)
306 << QByteArrayLiteral("SkipTaskbar");
307 QTest::newRow("SkipSwitcher") << int(Clt::PlasmaWindowModel::SkipSwitcher)
308 << QByteArrayLiteral("SkipSwitcher");
309 QTest::newRow("IsShadeable") << int(Clt::PlasmaWindowModel::IsShadeable)
310 << QByteArrayLiteral("IsShadeable");
311 QTest::newRow("IsShaded") << int(Clt::PlasmaWindowModel::IsShaded)
312 << QByteArrayLiteral("IsShaded");
313 QTest::newRow("IsMovable") << int(Clt::PlasmaWindowModel::IsMovable)
314 << QByteArrayLiteral("IsMovable");
315 QTest::newRow("IsResizable") << int(Clt::PlasmaWindowModel::IsResizable)
316 << QByteArrayLiteral("IsResizable");
317 QTest::newRow("IsVirtualDesktopChangeable")
318 << int(Clt::PlasmaWindowModel::IsVirtualDesktopChangeable)
319 << QByteArrayLiteral("IsVirtualDesktopChangeable");
320 QTest::newRow("IsCloseable") << int(Clt::PlasmaWindowModel::IsCloseable)
321 << QByteArrayLiteral("IsCloseable");
322 QTest::newRow("Geometry") << int(Clt::PlasmaWindowModel::Geometry)
323 << QByteArrayLiteral("Geometry");
324 }
325
testRoleNames()326 void PlasmaWindowModelTest::testRoleNames()
327 {
328 // Just verifies that all role names are available.
329 auto* model = m_pw->createWindowModel();
330 QVERIFY(model);
331
332 const QHash<int, QByteArray> roles = model->roleNames();
333
334 QFETCH(int, role);
335 auto it = roles.find(role);
336 QVERIFY(it != roles.end());
337 QTEST(it.value(), "name");
338 }
339
testAddRemoveRows()340 void PlasmaWindowModelTest::testAddRemoveRows()
341 {
342 // This test verifies that adding/removing rows to the Model works.
343 auto* model = m_pw->createWindowModel();
344 QVERIFY(model);
345
346 QCOMPARE(model->rowCount(), 0);
347 QVERIFY(!model->index(0).isValid());
348
349 // Now let's add a row.
350 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
351 QVERIFY(rowInsertedSpy.isValid());
352
353 // This happens by creating a PlasmaWindow on server side.
354 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
355 QVERIFY(serverWindow);
356
357 QVERIFY(rowInsertedSpy.wait());
358 QCOMPARE(rowInsertedSpy.count(), 1);
359 QVERIFY(!rowInsertedSpy.first().at(0).toModelIndex().isValid());
360 QCOMPARE(rowInsertedSpy.first().at(1).toInt(), 0);
361 QCOMPARE(rowInsertedSpy.first().at(2).toInt(), 0);
362
363 // The model should have a row now.
364 QCOMPARE(model->rowCount(), 1);
365 QVERIFY(model->index(0).isValid());
366 // That index doesn't have children.
367 QCOMPARE(model->rowCount(model->index(0)), 0);
368
369 // Process events in order to ensure that the resource is created on server side before we
370 // unmap the window.
371 QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents);
372
373 // Now let's remove that again.
374 QSignalSpy rowRemovedSpy(model, &Clt::PlasmaWindowModel::rowsRemoved);
375 QVERIFY(rowRemovedSpy.isValid());
376
377 QSignalSpy windowDestroyedSpy(serverWindow, &QObject::destroyed);
378 QVERIFY(windowDestroyedSpy.isValid());
379
380 serverWindow->unmap();
381 QVERIFY(rowRemovedSpy.wait());
382 QCOMPARE(rowRemovedSpy.count(), 1);
383 QVERIFY(!rowRemovedSpy.first().at(0).toModelIndex().isValid());
384 QCOMPARE(rowRemovedSpy.first().at(1).toInt(), 0);
385 QCOMPARE(rowRemovedSpy.first().at(2).toInt(), 0);
386
387 // Now the model is empty again.
388 QCOMPARE(model->rowCount(), 0);
389 QVERIFY(!model->index(0).isValid());
390
391 QCOMPARE(windowDestroyedSpy.count(), 1);
392 }
393
testDefaultData_data()394 void PlasmaWindowModelTest::testDefaultData_data()
395 {
396 QTest::addColumn<int>("role");
397 QTest::addColumn<QVariant>("value");
398
399 QTest::newRow("display") << int(Qt::DisplayRole) << QVariant(QString());
400 QTest::newRow("decoration") << int(Qt::DecorationRole) << QVariant(QIcon());
401
402 QTest::newRow("AppId") << int(Clt::PlasmaWindowModel::AppId) << QVariant(QString());
403 QTest::newRow("IsActive") << int(Clt::PlasmaWindowModel::IsActive) << QVariant(false);
404 QTest::newRow("IsFullscreenable")
405 << int(Clt::PlasmaWindowModel::IsFullscreenable) << QVariant(false);
406 QTest::newRow("IsFullscreen") << int(Clt::PlasmaWindowModel::IsFullscreen) << QVariant(false);
407 QTest::newRow("IsMaximizable") << int(Clt::PlasmaWindowModel::IsMaximizable) << QVariant(false);
408 QTest::newRow("IsMaximized") << int(Clt::PlasmaWindowModel::IsMaximized) << QVariant(false);
409 QTest::newRow("IsMinimizable") << int(Clt::PlasmaWindowModel::IsMinimizable) << QVariant(false);
410 QTest::newRow("IsMinimized") << int(Clt::PlasmaWindowModel::IsMinimized) << QVariant(false);
411 QTest::newRow("IsKeepAbove") << int(Clt::PlasmaWindowModel::IsKeepAbove) << QVariant(false);
412 QTest::newRow("IsKeepBelow") << int(Clt::PlasmaWindowModel::IsKeepBelow) << QVariant(false);
413 QTest::newRow("IsOnAllDesktops")
414 << int(Clt::PlasmaWindowModel::IsOnAllDesktops) << QVariant(true);
415 QTest::newRow("IsDemandingAttention")
416 << int(Clt::PlasmaWindowModel::IsDemandingAttention) << QVariant(false);
417 QTest::newRow("IsShadeable") << int(Clt::PlasmaWindowModel::IsShadeable) << QVariant(false);
418 QTest::newRow("IsShaded") << int(Clt::PlasmaWindowModel::IsShaded) << QVariant(false);
419 QTest::newRow("SkipTaskbar") << int(Clt::PlasmaWindowModel::SkipTaskbar) << QVariant(false);
420 QTest::newRow("IsMovable") << int(Clt::PlasmaWindowModel::IsMovable) << QVariant(false);
421 QTest::newRow("IsResizable") << int(Clt::PlasmaWindowModel::IsResizable) << QVariant(false);
422 QTest::newRow("IsVirtualDesktopChangeable")
423 << int(Clt::PlasmaWindowModel::IsVirtualDesktopChangeable) << QVariant(false);
424 QTest::newRow("IsCloseable") << int(Clt::PlasmaWindowModel::IsCloseable) << QVariant(false);
425 QTest::newRow("Geometry") << int(Clt::PlasmaWindowModel::Geometry) << QVariant(QRect());
426 QTest::newRow("Pid") << int(Clt::PlasmaWindowModel::Pid) << QVariant(0);
427 }
428
testDefaultData()429 void PlasmaWindowModelTest::testDefaultData()
430 {
431 // This test validates the default data of a PlasmaWindow without having set any values.
432 // First create a model with a window.
433 auto* model = m_pw->createWindowModel();
434 QVERIFY(model);
435
436 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
437 QVERIFY(rowInsertedSpy.isValid());
438
439 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
440 QVERIFY(serverWindow);
441 QVERIFY(rowInsertedSpy.wait());
442
443 QModelIndex index = model->index(0);
444 QFETCH(int, role);
445 QTEST(model->data(index, role), "value");
446 }
447
testIsActive()448 void PlasmaWindowModelTest::testIsActive()
449 {
450 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsActive, &Srv::PlasmaWindow::setActive));
451 }
452
testIsFullscreenable()453 void PlasmaWindowModelTest::testIsFullscreenable()
454 {
455 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsFullscreenable,
456 &Srv::PlasmaWindow::setFullscreenable));
457 }
458
testIsFullscreen()459 void PlasmaWindowModelTest::testIsFullscreen()
460 {
461 QVERIFY(
462 testBooleanData(Clt::PlasmaWindowModel::IsFullscreen, &Srv::PlasmaWindow::setFullscreen));
463 }
464
testIsMaximizable()465 void PlasmaWindowModelTest::testIsMaximizable()
466 {
467 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsMaximizable,
468 &Srv::PlasmaWindow::setMaximizeable));
469 }
470
testIsMaximized()471 void PlasmaWindowModelTest::testIsMaximized()
472 {
473 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsMaximized, &Srv::PlasmaWindow::setMaximized));
474 }
475
testIsMinimizable()476 void PlasmaWindowModelTest::testIsMinimizable()
477 {
478 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsMinimizable,
479 &Srv::PlasmaWindow::setMinimizeable));
480 }
481
testIsMinimized()482 void PlasmaWindowModelTest::testIsMinimized()
483 {
484 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsMinimized, &Srv::PlasmaWindow::setMinimized));
485 }
486
testIsKeepAbove()487 void PlasmaWindowModelTest::testIsKeepAbove()
488 {
489 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsKeepAbove, &Srv::PlasmaWindow::setKeepAbove));
490 }
491
testIsKeepBelow()492 void PlasmaWindowModelTest::testIsKeepBelow()
493 {
494 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsKeepBelow, &Srv::PlasmaWindow::setKeepBelow));
495 }
496
testIsDemandingAttention()497 void PlasmaWindowModelTest::testIsDemandingAttention()
498 {
499 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsDemandingAttention,
500 &Srv::PlasmaWindow::setDemandsAttention));
501 }
502
testSkipTaskbar()503 void PlasmaWindowModelTest::testSkipTaskbar()
504 {
505 QVERIFY(
506 testBooleanData(Clt::PlasmaWindowModel::SkipTaskbar, &Srv::PlasmaWindow::setSkipTaskbar));
507 }
508
testSkipSwitcher()509 void PlasmaWindowModelTest::testSkipSwitcher()
510 {
511 QVERIFY(
512 testBooleanData(Clt::PlasmaWindowModel::SkipSwitcher, &Srv::PlasmaWindow::setSkipSwitcher));
513 }
514
testIsShadeable()515 void PlasmaWindowModelTest::testIsShadeable()
516 {
517 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsShadeable, &Srv::PlasmaWindow::setShadeable));
518 }
519
testIsShaded()520 void PlasmaWindowModelTest::testIsShaded()
521 {
522 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsShaded, &Srv::PlasmaWindow::setShaded));
523 }
524
testIsMovable()525 void PlasmaWindowModelTest::testIsMovable()
526 {
527 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsMovable, &Srv::PlasmaWindow::setMovable));
528 }
529
testIsResizable()530 void PlasmaWindowModelTest::testIsResizable()
531 {
532 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsResizable, &Srv::PlasmaWindow::setResizable));
533 }
534
testIsVirtualDesktopChangeable()535 void PlasmaWindowModelTest::testIsVirtualDesktopChangeable()
536 {
537 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsVirtualDesktopChangeable,
538 &Srv::PlasmaWindow::setVirtualDesktopChangeable));
539 }
540
testIsCloseable()541 void PlasmaWindowModelTest::testIsCloseable()
542 {
543 QVERIFY(testBooleanData(Clt::PlasmaWindowModel::IsCloseable, &Srv::PlasmaWindow::setCloseable));
544 }
545
testGeometry()546 void PlasmaWindowModelTest::testGeometry()
547 {
548 auto* model = m_pw->createWindowModel();
549 QVERIFY(model);
550
551 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
552 QVERIFY(rowInsertedSpy.isValid());
553
554 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
555 QVERIFY(serverWindow);
556 QVERIFY(rowInsertedSpy.wait());
557
558 const QModelIndex index = model->index(0);
559
560 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::Geometry).toRect(), QRect());
561
562 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
563 QVERIFY(dataChangedSpy.isValid());
564
565 const QRect geom(0, 15, 50, 75);
566 serverWindow->setGeometry(geom);
567
568 QVERIFY(dataChangedSpy.wait());
569
570 // An icon and the geometry will be sent.
571 QTRY_COMPARE(dataChangedSpy.count(), 2);
572 QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index);
573
574 // The icon is received with QtConcurrent in the beginning. So it can arrive before or after
575 // the geometry.
576 const bool last
577 = dataChangedSpy[0].last().value<QVector<int>>() == QVector<int>{int(Qt::DecorationRole)};
578
579 QCOMPARE(dataChangedSpy[last ? 1 : 0].last().value<QVector<int>>(),
580 QVector<int>{int(Clt::PlasmaWindowModel::Geometry)});
581
582 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::Geometry).toRect(), geom);
583 }
584
testTitle()585 void PlasmaWindowModelTest::testTitle()
586 {
587 auto* model = m_pw->createWindowModel();
588 QVERIFY(model);
589
590 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
591 QVERIFY(rowInsertedSpy.isValid());
592
593 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
594 QVERIFY(serverWindow);
595 QVERIFY(rowInsertedSpy.wait());
596
597 m_connection->flush();
598 m_display->dispatchEvents();
599
600 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
601 QVERIFY(dataChangedSpy.isValid());
602
603 const QModelIndex index = model->index(0);
604 QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QString());
605
606 serverWindow->setTitle(QStringLiteral("foo"));
607 QVERIFY(dataChangedSpy.wait());
608
609 // An icon and the title will be sent.
610 QTRY_COMPARE(dataChangedSpy.count(), 2);
611 QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index);
612
613 // The icon is received with QtConcurrent in the beginning. So it can arrive before or after
614 // the title.
615 const bool last
616 = dataChangedSpy[0].last().value<QVector<int>>() == QVector<int>{int(Qt::DecorationRole)};
617
618 QCOMPARE(dataChangedSpy[last ? 1 : 0].last().value<QVector<int>>(),
619 QVector<int>{int(Qt::DisplayRole)});
620 QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QStringLiteral("foo"));
621 }
622
testAppId()623 void PlasmaWindowModelTest::testAppId()
624 {
625 auto* model = m_pw->createWindowModel();
626 QVERIFY(model);
627
628 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
629 QVERIFY(rowInsertedSpy.isValid());
630
631 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
632 QVERIFY(serverWindow);
633 QVERIFY(rowInsertedSpy.wait());
634
635 m_connection->flush();
636 m_display->dispatchEvents();
637
638 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
639 QVERIFY(dataChangedSpy.isValid());
640
641 const QModelIndex index = model->index(0);
642 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::AppId).toString(), QString());
643
644 serverWindow->setAppId(QStringLiteral("org.kde.testapp"));
645 QVERIFY(dataChangedSpy.count() || dataChangedSpy.wait());
646
647 // The App Id and the geometry will be sent.
648 QTRY_COMPARE(dataChangedSpy.count(), 2);
649 QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index);
650
651 // The icon is received with QtConcurrent in the beginning. So it can arrive before or after
652 // the app id.
653 const bool last
654 = dataChangedSpy[0].last().value<QVector<int>>() == QVector<int>{int(Qt::DecorationRole)};
655
656 QCOMPARE(dataChangedSpy[last ? 1 : 0].last().value<QVector<int>>(),
657 QVector<int>{int(Clt::PlasmaWindowModel::AppId)});
658 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::AppId).toString(),
659 QStringLiteral("org.kde.testapp"));
660 }
661
testPid()662 void PlasmaWindowModelTest::testPid()
663 {
664 auto* model = m_pw->createWindowModel();
665 QVERIFY(model);
666
667 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
668 QVERIFY(rowInsertedSpy.isValid());
669
670 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
671 serverWindow->setPid(1337);
672 QVERIFY(serverWindow);
673
674 m_connection->flush();
675 m_display->dispatchEvents();
676 QVERIFY(rowInsertedSpy.wait());
677
678 // pid should be set as soon as the new row appears
679 const QModelIndex index = model->index(0);
680 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::Pid).toInt(), 1337);
681 }
682
testVirtualDesktops()683 void PlasmaWindowModelTest::testVirtualDesktops()
684 {
685 auto* model = m_pw->createWindowModel();
686 QVERIFY(model);
687
688 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
689 QVERIFY(rowInsertedSpy.isValid());
690 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
691 QVERIFY(serverWindow);
692 QVERIFY(rowInsertedSpy.wait());
693
694 m_connection->flush();
695 m_display->dispatchEvents();
696 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
697 QVERIFY(dataChangedSpy.isValid());
698
699 const QModelIndex index = model->index(0);
700 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::VirtualDesktops).toStringList(),
701 QStringList());
702
703 serverWindow->addPlasmaVirtualDesktop("desktop1");
704 QVERIFY(dataChangedSpy.wait());
705 QTRY_COMPARE(dataChangedSpy.count(), 3);
706
707 QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index);
708 QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index);
709
710 // The icon is received with QtConcurrent in the beginning. So it can arrive before or after
711 // the virtual desktop.
712 const bool last
713 = dataChangedSpy[0].last().value<QVector<int>>() == QVector<int>{int(Qt::DecorationRole)};
714
715 QCOMPARE(dataChangedSpy[last ? 1 : 0].last().value<QVector<int>>(),
716 QVector<int>{int(Clt::PlasmaWindowModel::VirtualDesktops)});
717 QCOMPARE(dataChangedSpy[last ? 2 : 1].last().value<QVector<int>>(),
718 QVector<int>{int(Clt::PlasmaWindowModel::IsOnAllDesktops)});
719
720 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::VirtualDesktops).toStringList(),
721 QStringList({"desktop1"}));
722 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::IsOnAllDesktops).toBool(), false);
723
724 dataChangedSpy.clear();
725 QVERIFY(!dataChangedSpy.count());
726
727 serverWindow->addPlasmaVirtualDesktop("desktop2");
728 QVERIFY(dataChangedSpy.wait());
729 QTRY_COMPARE(dataChangedSpy.count(), 1);
730
731 QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index);
732 QCOMPARE(dataChangedSpy.first().last().value<QVector<int>>(),
733 QVector<int>{int(Clt::PlasmaWindowModel::VirtualDesktops)});
734
735 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::VirtualDesktops).toStringList(),
736 QStringList({"desktop1", "desktop2"}));
737 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::IsOnAllDesktops).toBool(), false);
738
739 serverWindow->removePlasmaVirtualDesktop("desktop2");
740 serverWindow->removePlasmaVirtualDesktop("desktop1");
741
742 QVERIFY(dataChangedSpy.wait());
743 QTRY_COMPARE(dataChangedSpy.count(), 5);
744
745 QCOMPARE(dataChangedSpy.last().last().value<QVector<int>>(),
746 QVector<int>{int(Clt::PlasmaWindowModel::IsOnAllDesktops)});
747
748 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::VirtualDesktops).toStringList(),
749 QStringList({}));
750 QCOMPARE(model->data(index, Clt::PlasmaWindowModel::IsOnAllDesktops).toBool(), true);
751 QVERIFY(!dataChangedSpy.wait(100));
752 }
753
testRequests()754 void PlasmaWindowModelTest::testRequests()
755 {
756 // This test verifies that the various requests are properly passed to the server.
757 auto* model = m_pw->createWindowModel();
758 QVERIFY(model);
759
760 QSignalSpy rowInsertedSpy(model, &Clt::PlasmaWindowModel::rowsInserted);
761 QVERIFY(rowInsertedSpy.isValid());
762
763 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
764 QVERIFY(serverWindow);
765 QVERIFY(rowInsertedSpy.wait());
766
767 QSignalSpy activateRequestedSpy(serverWindow, &Srv::PlasmaWindow::activeRequested);
768 QVERIFY(activateRequestedSpy.isValid());
769 QSignalSpy closeRequestedSpy(serverWindow, &Srv::PlasmaWindow::closeRequested);
770 QVERIFY(closeRequestedSpy.isValid());
771 QSignalSpy moveRequestedSpy(serverWindow, &Srv::PlasmaWindow::moveRequested);
772 QVERIFY(moveRequestedSpy.isValid());
773 QSignalSpy resizeRequestedSpy(serverWindow, &Srv::PlasmaWindow::resizeRequested);
774 QVERIFY(resizeRequestedSpy.isValid());
775 QSignalSpy keepAboveRequestedSpy(serverWindow, &Srv::PlasmaWindow::keepAboveRequested);
776 QVERIFY(keepAboveRequestedSpy.isValid());
777 QSignalSpy keepBelowRequestedSpy(serverWindow, &Srv::PlasmaWindow::keepBelowRequested);
778 QVERIFY(keepBelowRequestedSpy.isValid());
779 QSignalSpy minimizedRequestedSpy(serverWindow, &Srv::PlasmaWindow::minimizedRequested);
780 QVERIFY(minimizedRequestedSpy.isValid());
781 QSignalSpy maximizeRequestedSpy(serverWindow, &Srv::PlasmaWindow::maximizedRequested);
782 QVERIFY(maximizeRequestedSpy.isValid());
783 QSignalSpy shadeRequestedSpy(serverWindow, &Srv::PlasmaWindow::shadedRequested);
784 QVERIFY(shadeRequestedSpy.isValid());
785
786 // First let's use some invalid row numbers.
787 model->requestActivate(-1);
788 model->requestClose(-1);
789 model->requestToggleKeepAbove(-1);
790 model->requestToggleKeepBelow(-1);
791 model->requestToggleMinimized(-1);
792 model->requestToggleMaximized(-1);
793 model->requestActivate(1);
794 model->requestClose(1);
795 model->requestMove(1);
796 model->requestResize(1);
797 model->requestToggleKeepAbove(1);
798 model->requestToggleKeepBelow(1);
799 model->requestToggleMinimized(1);
800 model->requestToggleMaximized(1);
801 model->requestToggleShaded(1);
802
803 // That should not have triggered any signals.
804 QVERIFY(!activateRequestedSpy.wait(100));
805 QVERIFY(activateRequestedSpy.isEmpty());
806 QVERIFY(closeRequestedSpy.isEmpty());
807 QVERIFY(moveRequestedSpy.isEmpty());
808 QVERIFY(resizeRequestedSpy.isEmpty());
809 QVERIFY(minimizedRequestedSpy.isEmpty());
810 QVERIFY(maximizeRequestedSpy.isEmpty());
811 QVERIFY(shadeRequestedSpy.isEmpty());
812
813 // Now with the proper row.
814 // Activate
815 model->requestActivate(0);
816 QVERIFY(activateRequestedSpy.wait());
817 QCOMPARE(activateRequestedSpy.count(), 1);
818 QCOMPARE(activateRequestedSpy.first().first().toBool(), true);
819 QCOMPARE(closeRequestedSpy.count(), 0);
820 QCOMPARE(moveRequestedSpy.count(), 0);
821 QCOMPARE(resizeRequestedSpy.count(), 0);
822 QCOMPARE(minimizedRequestedSpy.count(), 0);
823 QCOMPARE(maximizeRequestedSpy.count(), 0);
824 QCOMPARE(shadeRequestedSpy.count(), 0);
825 // Close
826 model->requestClose(0);
827 QVERIFY(closeRequestedSpy.wait());
828 QCOMPARE(activateRequestedSpy.count(), 1);
829 QCOMPARE(closeRequestedSpy.count(), 1);
830 QCOMPARE(moveRequestedSpy.count(), 0);
831 QCOMPARE(resizeRequestedSpy.count(), 0);
832 QCOMPARE(minimizedRequestedSpy.count(), 0);
833 QCOMPARE(maximizeRequestedSpy.count(), 0);
834 QCOMPARE(shadeRequestedSpy.count(), 0);
835 // Move
836 model->requestMove(0);
837 QVERIFY(moveRequestedSpy.wait());
838 QCOMPARE(activateRequestedSpy.count(), 1);
839 QCOMPARE(closeRequestedSpy.count(), 1);
840 QCOMPARE(moveRequestedSpy.count(), 1);
841 QCOMPARE(resizeRequestedSpy.count(), 0);
842 QCOMPARE(minimizedRequestedSpy.count(), 0);
843 QCOMPARE(maximizeRequestedSpy.count(), 0);
844 QCOMPARE(shadeRequestedSpy.count(), 0);
845 // Resize
846 model->requestResize(0);
847 QVERIFY(resizeRequestedSpy.wait());
848 QCOMPARE(activateRequestedSpy.count(), 1);
849 QCOMPARE(closeRequestedSpy.count(), 1);
850 QCOMPARE(moveRequestedSpy.count(), 1);
851 QCOMPARE(resizeRequestedSpy.count(), 1);
852 QCOMPARE(minimizedRequestedSpy.count(), 0);
853 QCOMPARE(maximizeRequestedSpy.count(), 0);
854 QCOMPARE(shadeRequestedSpy.count(), 0);
855 // Virtual desktop
856 QCOMPARE(activateRequestedSpy.count(), 1);
857 QCOMPARE(closeRequestedSpy.count(), 1);
858 QCOMPARE(moveRequestedSpy.count(), 1);
859 QCOMPARE(resizeRequestedSpy.count(), 1);
860 QCOMPARE(minimizedRequestedSpy.count(), 0);
861 QCOMPARE(maximizeRequestedSpy.count(), 0);
862 QCOMPARE(shadeRequestedSpy.count(), 0);
863 // Keep above
864 model->requestToggleKeepAbove(0);
865 QVERIFY(keepAboveRequestedSpy.wait());
866 QCOMPARE(keepAboveRequestedSpy.count(), 1);
867 QCOMPARE(keepAboveRequestedSpy.first().first().toBool(), true);
868 QCOMPARE(activateRequestedSpy.count(), 1);
869 QCOMPARE(closeRequestedSpy.count(), 1);
870 QCOMPARE(moveRequestedSpy.count(), 1);
871 QCOMPARE(resizeRequestedSpy.count(), 1);
872 QCOMPARE(maximizeRequestedSpy.count(), 0);
873 QCOMPARE(shadeRequestedSpy.count(), 0);
874 // Keep below
875 model->requestToggleKeepBelow(0);
876 QVERIFY(keepBelowRequestedSpy.wait());
877 QCOMPARE(keepBelowRequestedSpy.count(), 1);
878 QCOMPARE(keepBelowRequestedSpy.first().first().toBool(), true);
879 QCOMPARE(activateRequestedSpy.count(), 1);
880 QCOMPARE(closeRequestedSpy.count(), 1);
881 QCOMPARE(moveRequestedSpy.count(), 1);
882 QCOMPARE(resizeRequestedSpy.count(), 1);
883 QCOMPARE(maximizeRequestedSpy.count(), 0);
884 QCOMPARE(shadeRequestedSpy.count(), 0);
885 // Minimize
886 model->requestToggleMinimized(0);
887 QVERIFY(minimizedRequestedSpy.wait());
888 QCOMPARE(minimizedRequestedSpy.count(), 1);
889 QCOMPARE(minimizedRequestedSpy.first().first().toBool(), true);
890 QCOMPARE(activateRequestedSpy.count(), 1);
891 QCOMPARE(closeRequestedSpy.count(), 1);
892 QCOMPARE(moveRequestedSpy.count(), 1);
893 QCOMPARE(resizeRequestedSpy.count(), 1);
894 QCOMPARE(maximizeRequestedSpy.count(), 0);
895 QCOMPARE(shadeRequestedSpy.count(), 0);
896 // Maximize
897 model->requestToggleMaximized(0);
898 QVERIFY(maximizeRequestedSpy.wait());
899 QCOMPARE(maximizeRequestedSpy.count(), 1);
900 QCOMPARE(maximizeRequestedSpy.first().first().toBool(), true);
901 QCOMPARE(activateRequestedSpy.count(), 1);
902 QCOMPARE(closeRequestedSpy.count(), 1);
903 QCOMPARE(moveRequestedSpy.count(), 1);
904 QCOMPARE(minimizedRequestedSpy.count(), 1);
905 QCOMPARE(shadeRequestedSpy.count(), 0);
906 // Shade
907 model->requestToggleShaded(0);
908 QVERIFY(shadeRequestedSpy.wait());
909 QCOMPARE(shadeRequestedSpy.count(), 1);
910 QCOMPARE(shadeRequestedSpy.first().first().toBool(), true);
911 QCOMPARE(activateRequestedSpy.count(), 1);
912 QCOMPARE(closeRequestedSpy.count(), 1);
913 QCOMPARE(moveRequestedSpy.count(), 1);
914 QCOMPARE(resizeRequestedSpy.count(), 1);
915 QCOMPARE(minimizedRequestedSpy.count(), 1);
916 QCOMPARE(maximizeRequestedSpy.count(), 1);
917
918 // The toggles can also support a different state.
919 QSignalSpy dataChangedSpy(model, &Clt::PlasmaWindowModel::dataChanged);
920 QVERIFY(dataChangedSpy.isValid());
921 // Keep above
922 serverWindow->setKeepAbove(true);
923 QVERIFY(dataChangedSpy.wait());
924 model->requestToggleKeepAbove(0);
925 QVERIFY(keepAboveRequestedSpy.wait());
926 QCOMPARE(keepAboveRequestedSpy.count(), 2);
927 QCOMPARE(keepAboveRequestedSpy.last().first().toBool(), false);
928 // Keep below
929 serverWindow->setKeepBelow(true);
930 QVERIFY(dataChangedSpy.wait());
931 model->requestToggleKeepBelow(0);
932 QVERIFY(keepBelowRequestedSpy.wait());
933 QCOMPARE(keepBelowRequestedSpy.count(), 2);
934 QCOMPARE(keepBelowRequestedSpy.last().first().toBool(), false);
935 // Minimize
936 serverWindow->setMinimized(true);
937 QVERIFY(dataChangedSpy.wait());
938 model->requestToggleMinimized(0);
939 QVERIFY(minimizedRequestedSpy.wait());
940 QCOMPARE(minimizedRequestedSpy.count(), 2);
941 QCOMPARE(minimizedRequestedSpy.last().first().toBool(), false);
942 // Maximized
943 serverWindow->setMaximized(true);
944 QVERIFY(dataChangedSpy.wait());
945 model->requestToggleMaximized(0);
946 QVERIFY(maximizeRequestedSpy.wait());
947 QCOMPARE(maximizeRequestedSpy.count(), 2);
948 QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false);
949 // Shaded
950 serverWindow->setShaded(true);
951 QVERIFY(dataChangedSpy.wait());
952 model->requestToggleShaded(0);
953 QVERIFY(shadeRequestedSpy.wait());
954 QCOMPARE(shadeRequestedSpy.count(), 2);
955 QCOMPARE(shadeRequestedSpy.last().first().toBool(), false);
956 }
957
testCreateWithUnmappedWindow()958 void PlasmaWindowModelTest::testCreateWithUnmappedWindow()
959 {
960 // This test verifies that creating the model just when an unmapped window exists doesn't cause
961 // problems.
962 // That is the unmapped window should be added (as expected), but also be removed again.
963
964 // Create a window in "normal way".
965 QSignalSpy windowCreatedSpy(m_pw, &Clt::PlasmaWindowManagement::windowCreated);
966 QVERIFY(windowCreatedSpy.isValid());
967
968 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
969 QVERIFY(serverWindow);
970 QVERIFY(windowCreatedSpy.wait());
971
972 auto* window = windowCreatedSpy.first().first().value<Clt::PlasmaWindow*>();
973 QVERIFY(window);
974
975 // Make sure the resource is properly created on server side.
976 QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents);
977
978 QSignalSpy unmappedSpy(window, &Clt::PlasmaWindow::unmapped);
979 QVERIFY(unmappedSpy.isValid());
980 QSignalSpy destroyedSpy(window, &Clt::PlasmaWindow::destroyed);
981 QVERIFY(destroyedSpy.isValid());
982
983 // Unmap should be triggered, but not yet the destroyed.
984 serverWindow->unmap();
985 QVERIFY(unmappedSpy.wait());
986 QVERIFY(destroyedSpy.isEmpty());
987
988 auto* model = m_pw->createWindowModel();
989 QVERIFY(model);
990 QCOMPARE(model->rowCount(), 1);
991 QSignalSpy rowRemovedSpy(model, &Clt::PlasmaWindowModel::rowsRemoved);
992 QVERIFY(rowRemovedSpy.isValid());
993 QVERIFY(rowRemovedSpy.wait());
994 QCOMPARE(rowRemovedSpy.count(), 1);
995 QCOMPARE(model->rowCount(), 0);
996 QCOMPARE(destroyedSpy.count(), 1);
997 }
998
testChangeWindowAfterModelDestroy_data()999 void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy_data()
1000 {
1001 QTest::addColumn<ClientWindowSignal>("changedSignal");
1002 QTest::addColumn<QVariant>("setter");
1003 QTest::addColumn<QVariant>("value");
1004
1005 QTest::newRow("active") << &Clt::PlasmaWindow::activeChanged
1006 << QVariant::fromValue(&Srv::PlasmaWindow::setActive) << QVariant(true);
1007 QTest::newRow("minimized") << &Clt::PlasmaWindow::minimizedChanged
1008 << QVariant::fromValue(&Srv::PlasmaWindow::setMinimized)
1009 << QVariant(true);
1010 QTest::newRow("fullscreen") << &Clt::PlasmaWindow::fullscreenChanged
1011 << QVariant::fromValue(&Srv::PlasmaWindow::setFullscreen)
1012 << QVariant(true);
1013 QTest::newRow("keepAbove") << &Clt::PlasmaWindow::keepAboveChanged
1014 << QVariant::fromValue(&Srv::PlasmaWindow::setKeepAbove)
1015 << QVariant(true);
1016 QTest::newRow("keepBelow") << &Clt::PlasmaWindow::keepBelowChanged
1017 << QVariant::fromValue(&Srv::PlasmaWindow::setKeepBelow)
1018 << QVariant(true);
1019 QTest::newRow("maximized") << &Clt::PlasmaWindow::maximizedChanged
1020 << QVariant::fromValue(&Srv::PlasmaWindow::setMaximized)
1021 << QVariant(true);
1022 QTest::newRow("demandsAttention")
1023 << &Clt::PlasmaWindow::demandsAttentionChanged
1024 << QVariant::fromValue(&Srv::PlasmaWindow::setDemandsAttention) << QVariant(true);
1025 QTest::newRow("closeable") << &Clt::PlasmaWindow::closeableChanged
1026 << QVariant::fromValue(&Srv::PlasmaWindow::setCloseable)
1027 << QVariant(true);
1028 QTest::newRow("minimizeable") << &Clt::PlasmaWindow::minimizeableChanged
1029 << QVariant::fromValue(&Srv::PlasmaWindow::setMinimizeable)
1030 << QVariant(true);
1031 QTest::newRow("maximizeable") << &Clt::PlasmaWindow::maximizeableChanged
1032 << QVariant::fromValue(&Srv::PlasmaWindow::setMaximizeable)
1033 << QVariant(true);
1034 QTest::newRow("fullscreenable")
1035 << &Clt::PlasmaWindow::fullscreenableChanged
1036 << QVariant::fromValue(&Srv::PlasmaWindow::setFullscreenable) << QVariant(true);
1037 QTest::newRow("skipTaskbar") << &Clt::PlasmaWindow::skipTaskbarChanged
1038 << QVariant::fromValue(&Srv::PlasmaWindow::setSkipTaskbar)
1039 << QVariant(true);
1040 QTest::newRow("shadeable") << &Clt::PlasmaWindow::shadeableChanged
1041 << QVariant::fromValue(&Srv::PlasmaWindow::setShadeable)
1042 << QVariant(true);
1043 QTest::newRow("shaded") << &Clt::PlasmaWindow::shadedChanged
1044 << QVariant::fromValue(&Srv::PlasmaWindow::setShaded) << QVariant(true);
1045 QTest::newRow("movable") << &Clt::PlasmaWindow::movableChanged
1046 << QVariant::fromValue(&Srv::PlasmaWindow::setMovable)
1047 << QVariant(true);
1048 QTest::newRow("resizable") << &Clt::PlasmaWindow::resizableChanged
1049 << QVariant::fromValue(&Srv::PlasmaWindow::setResizable)
1050 << QVariant(true);
1051 QTest::newRow("vdChangeable") << &Clt::PlasmaWindow::virtualDesktopChangeableChanged
1052 << QVariant::fromValue(
1053 &Srv::PlasmaWindow::setVirtualDesktopChangeable)
1054 << QVariant(true);
1055 QTest::newRow("onallDesktop") << &Clt::PlasmaWindow::onAllDesktopsChanged
1056 << QVariant::fromValue(&Srv::PlasmaWindow::setOnAllDesktops)
1057 << QVariant(true);
1058 QTest::newRow("title") << &Clt::PlasmaWindow::titleChanged
1059 << QVariant::fromValue(&Srv::PlasmaWindow::setTitle)
1060 << QVariant(QStringLiteral("foo"));
1061 QTest::newRow("appId") << &Clt::PlasmaWindow::appIdChanged
1062 << QVariant::fromValue(&Srv::PlasmaWindow::setAppId)
1063 << QVariant(QStringLiteral("foo"));
1064
1065 // Disable the icon test for now. Our way of providing icons is fundamentally wrong and the
1066 // whole concept needs to be redone so it works on all setups and in particular in a CI setting.
1067 // See issue #8.
1068 #if 0
1069 QTest::newRow("icon" )
1070 << &Clt::PlasmaWindow::iconChanged
1071 << QVariant::fromValue(&Srv::PlasmaWindow::setIcon)
1072 << QVariant::fromValue(QIcon::fromTheme(QStringLiteral("foo")));
1073 #endif
1074
1075 QTest::newRow("unmapped") << &Clt::PlasmaWindow::unmapped
1076 << QVariant::fromValue(&Srv::PlasmaWindow::unmap) << QVariant();
1077 }
1078
testChangeWindowAfterModelDestroy()1079 void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy()
1080 {
1081 // This test verifies that changes in a window after the model got destroyed doesn't crash.
1082 auto* model = m_pw->createWindowModel();
1083 QVERIFY(model);
1084
1085 QSignalSpy windowCreatedSpy(m_pw, &Clt::PlasmaWindowManagement::windowCreated);
1086 QVERIFY(windowCreatedSpy.isValid());
1087
1088 auto* serverWindow = m_serverWindowManager->createWindow(m_serverWindowManager);
1089 QVERIFY(windowCreatedSpy.wait());
1090 Clt::PlasmaWindow* window = windowCreatedSpy.first().first().value<Clt::PlasmaWindow*>();
1091
1092 // Make sure the resource is properly created on server side.
1093 QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents);
1094 QCOMPARE(model->rowCount(), 1);
1095 delete model;
1096
1097 QFETCH(ClientWindowSignal, changedSignal);
1098 QSignalSpy changedSpy(window, changedSignal);
1099 QVERIFY(changedSpy.isValid());
1100 QVERIFY(!window->isActive());
1101
1102 QFETCH(QVariant, setter);
1103 QFETCH(QVariant, value);
1104
1105 if (QMetaType::Type(value.type()) == QMetaType::Bool) {
1106 (serverWindow->*(setter.value<ServerWindowBoolSetter>()))(value.toBool());
1107 } else if (QMetaType::Type(value.type()) == QMetaType::QString) {
1108 (serverWindow->*(setter.value<ServerWindowStringSetter>()))(value.toString());
1109 } else if (QMetaType::Type(value.type()) == QMetaType::UInt) {
1110 (serverWindow->*(setter.value<ServerWindowQuint32Setter>()))(value.toUInt());
1111 } else if (!value.isValid()) {
1112 (serverWindow->*(setter.value<ServerWindowVoidSetter>()))();
1113 }
1114
1115 QVERIFY(changedSpy.wait());
1116 }
1117
testCreateWindowAfterModelDestroy()1118 void PlasmaWindowModelTest::testCreateWindowAfterModelDestroy()
1119 {
1120 // This test verifies that creating a window after the model got destroyed doesn't crash.
1121 auto* model = m_pw->createWindowModel();
1122 QVERIFY(model);
1123
1124 delete model;
1125
1126 QSignalSpy windowCreatedSpy(m_pw, &Clt::PlasmaWindowManagement::windowCreated);
1127 QVERIFY(windowCreatedSpy.isValid());
1128
1129 m_serverWindowManager->createWindow(m_serverWindowManager);
1130 QVERIFY(windowCreatedSpy.wait());
1131 }
1132
1133 QTEST_GUILESS_MAIN(PlasmaWindowModelTest)
1134 #include "plasma_window_model.moc"
1135