diff --git a/autotests/client/test_plasma_window_model.cpp b/autotests/client/test_plasma_window_model.cpp --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -35,6 +35,16 @@ using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) +typedef void (KWayland::Client::PlasmaWindow::*ClientWindowSignal)(); +Q_DECLARE_METATYPE(ClientWindowSignal) +typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBoolSetter)(bool); +Q_DECLARE_METATYPE(ServerWindowBoolSetter) +typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowStringSetter)(const QString&); +Q_DECLARE_METATYPE(ServerWindowStringSetter) +typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowQuint32Setter)(quint32); +Q_DECLARE_METATYPE(ServerWindowQuint32Setter) +typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowVoidSetter)(); +Q_DECLARE_METATYPE(ServerWindowVoidSetter) class PlasmaWindowModelTest : public QObject { @@ -74,6 +84,9 @@ // TODO: minimized geometry // TODO: model reset void testCreateWithUnmappedWindow(); + void testChangeWindowAfterModelDestroy_data(); + void testChangeWindowAfterModelDestroy(); + void testCreateWindowAfterModelDestroy(); private: bool testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)); @@ -715,5 +728,79 @@ QCOMPARE(destroyedSpy.count(), 1); } +void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy_data() +{ + QTest::addColumn("changedSignal"); + QTest::addColumn("setter"); + QTest::addColumn("value"); + + QTest::newRow("active") << &PlasmaWindow::activeChanged << qVariantFromValue(&PlasmaWindowInterface::setActive) << QVariant(true); + QTest::newRow("minimized") << &PlasmaWindow::minimizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimized) << QVariant(true); + QTest::newRow("fullscreen") << &PlasmaWindow::fullscreenChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreen) << QVariant(true); + QTest::newRow("keepAbove") << &PlasmaWindow::keepAboveChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepAbove) << QVariant(true); + QTest::newRow("keepBelow") << &PlasmaWindow::keepBelowChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepBelow) << QVariant(true); + QTest::newRow("maximized") << &PlasmaWindow::maximizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximized) << QVariant(true); + QTest::newRow("demandsAttention") << &PlasmaWindow::demandsAttentionChanged << qVariantFromValue(&PlasmaWindowInterface::setDemandsAttention) << QVariant(true); + QTest::newRow("closeable") << &PlasmaWindow::closeableChanged << qVariantFromValue(&PlasmaWindowInterface::setCloseable) << QVariant(true); + QTest::newRow("minimizeable") << &PlasmaWindow::minimizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimizeable) << QVariant(true); + QTest::newRow("maximizeable") << &PlasmaWindow::maximizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximizeable) << QVariant(true); + QTest::newRow("fullscreenable") << &PlasmaWindow::fullscreenableChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreenable) << QVariant(true); + QTest::newRow("skipTaskbar") << &PlasmaWindow::skipTaskbarChanged << qVariantFromValue(&PlasmaWindowInterface::setSkipTaskbar) << QVariant(true); + QTest::newRow("shadeable") << &PlasmaWindow::shadeableChanged << qVariantFromValue(&PlasmaWindowInterface::setShadeable) << QVariant(true); + QTest::newRow("shaded") << &PlasmaWindow::shadedChanged << qVariantFromValue(&PlasmaWindowInterface::setShaded) << QVariant(true); + QTest::newRow("movable") << &PlasmaWindow::movableChanged << qVariantFromValue(&PlasmaWindowInterface::setMovable) << QVariant(true); + QTest::newRow("resizable") << &PlasmaWindow::resizableChanged << qVariantFromValue(&PlasmaWindowInterface::setResizable) << QVariant(true); + QTest::newRow("vdChangeable") << &PlasmaWindow::virtualDesktopChangeableChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktopChangeable) << QVariant(true); + QTest::newRow("onallDesktop") << &PlasmaWindow::onAllDesktopsChanged << qVariantFromValue(&PlasmaWindowInterface::setOnAllDesktops) << QVariant(true); + QTest::newRow("title") << &PlasmaWindow::titleChanged << qVariantFromValue(&PlasmaWindowInterface::setTitle) << QVariant(QStringLiteral("foo")); + QTest::newRow("appId") << &PlasmaWindow::appIdChanged << qVariantFromValue(&PlasmaWindowInterface::setAppId) << QVariant(QStringLiteral("foo")); + QTest::newRow("icon" ) << &PlasmaWindow::iconChanged << qVariantFromValue(&PlasmaWindowInterface::setThemedIconName) << QVariant(QStringLiteral("foo")); + QTest::newRow("vd") << &PlasmaWindow::virtualDesktopChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktop) << QVariant(2u); + QTest::newRow("unmapped") << &PlasmaWindow::unmapped << qVariantFromValue(&PlasmaWindowInterface::unmap) << QVariant(); +} + +void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy() +{ + // this test verifies that changes in a window after the model got destroyed doesn't crash + auto model = m_pw->createWindowModel(); + QVERIFY(model); + QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); + QVERIFY(windowCreatedSpy.isValid()); + auto w = m_pwInterface->createWindow(m_pwInterface); + QVERIFY(windowCreatedSpy.wait()); + PlasmaWindow *window = windowCreatedSpy.first().first().value(); + QCOMPARE(model->rowCount(), 1); + delete model; + QFETCH(ClientWindowSignal, changedSignal); + QSignalSpy changedSpy(window, changedSignal); + QVERIFY(changedSpy.isValid()); + QVERIFY(!window->isActive()); + QFETCH(QVariant, setter); + QFETCH(QVariant, value); + if (QMetaType::Type(value.type()) == QMetaType::Bool) { + (w->*(setter.value()))(value.toBool()); + } else if (QMetaType::Type(value.type()) == QMetaType::QString) { + (w->*(setter.value()))(value.toString()); + } else if (QMetaType::Type(value.type()) == QMetaType::UInt) { + (w->*(setter.value()))(value.toUInt()); + } else if (!value.isValid()) { + (w->*(setter.value()))(); + } + + QVERIFY(changedSpy.wait()); +} + +void PlasmaWindowModelTest::testCreateWindowAfterModelDestroy() +{ + // this test verifies that creating a window after the model got destroyed doesn't crash + auto model = m_pw->createWindowModel(); + QVERIFY(model); + delete model; + QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); + QVERIFY(windowCreatedSpy.isValid()); + m_pwInterface->createWindow(m_pwInterface); + QVERIFY(windowCreatedSpy.wait()); +} + QTEST_GUILESS_MAIN(PlasmaWindowModelTest) #include "test_plasma_window_model.moc" diff --git a/src/client/plasmawindowmodel.cpp b/src/client/plasmawindowmodel.cpp --- a/src/client/plasmawindowmodel.cpp +++ b/src/client/plasmawindowmodel.cpp @@ -69,91 +69,91 @@ QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); QObject::connect(window, &QObject::destroyed, q, removeWindow); - QObject::connect(window, &PlasmaWindow::titleChanged, + QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { this->dataChanged(window, Qt::DisplayRole); } ); - QObject::connect(window, &PlasmaWindow::iconChanged, + QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { this->dataChanged(window, Qt::DecorationRole); } ); - QObject::connect(window, &PlasmaWindow::appIdChanged, + QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { this->dataChanged(window, PlasmaWindowModel::AppId); } ); - QObject::connect(window, &PlasmaWindow::activeChanged, + QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { this->dataChanged(window, IsActive); } ); - QObject::connect(window, &PlasmaWindow::fullscreenableChanged, + QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { this->dataChanged(window, IsFullscreenable); } ); - QObject::connect(window, &PlasmaWindow::fullscreenChanged, + QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { this->dataChanged(window, IsFullscreen); } ); - QObject::connect(window, &PlasmaWindow::maximizeableChanged, + QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { this->dataChanged(window, IsMaximizable); } ); - QObject::connect(window, &PlasmaWindow::maximizedChanged, + QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { this->dataChanged(window, IsMaximized); } ); - QObject::connect(window, &PlasmaWindow::minimizeableChanged, + QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { this->dataChanged(window, IsMinimizable); } ); - QObject::connect(window, &PlasmaWindow::minimizedChanged, + QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { this->dataChanged(window, IsMinimized); } ); - QObject::connect(window, &PlasmaWindow::keepAboveChanged, + QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { this->dataChanged(window, IsKeepAbove); } ); - QObject::connect(window, &PlasmaWindow::keepBelowChanged, + QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { this->dataChanged(window, IsKeepBelow); } ); - QObject::connect(window, &PlasmaWindow::virtualDesktopChanged, + QObject::connect(window, &PlasmaWindow::virtualDesktopChanged, q, [window, this] { this->dataChanged(window, VirtualDesktop); } ); - QObject::connect(window, &PlasmaWindow::onAllDesktopsChanged, + QObject::connect(window, &PlasmaWindow::onAllDesktopsChanged, q, [window, this] { this->dataChanged(window, IsOnAllDesktops); } ); - QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, + QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { this->dataChanged(window, IsDemandingAttention); } ); - QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, + QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { this->dataChanged(window, SkipTaskbar); } ); - QObject::connect(window, &PlasmaWindow::shadeableChanged, + QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { this->dataChanged(window, IsShadeable); } ); - QObject::connect(window, &PlasmaWindow::shadedChanged, + QObject::connect(window, &PlasmaWindow::shadedChanged, q, [window, this] { this->dataChanged(window, IsShaded); } ); - QObject::connect(window, &PlasmaWindow::movableChanged, + QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { this->dataChanged(window, IsMovable); } ); - QObject::connect(window, &PlasmaWindow::resizableChanged, + QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { this->dataChanged(window, IsResizable); } ); - QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, + QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { this->dataChanged(window, IsVirtualDesktopChangeable); } ); - QObject::connect(window, &PlasmaWindow::closeableChanged, + QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { this->dataChanged(window, IsCloseable); } ); } @@ -168,15 +168,15 @@ : QAbstractListModel(parent) , d(new Private(this)) { - connect(parent, &PlasmaWindowManagement::interfaceAboutToBeReleased, + connect(parent, &PlasmaWindowManagement::interfaceAboutToBeReleased, this, [this] { beginResetModel(); d->windows.clear(); endResetModel(); } ); - connect(parent, &PlasmaWindowManagement::windowCreated, + connect(parent, &PlasmaWindowManagement::windowCreated, this, [this](PlasmaWindow *window) { d->addWindow(window); }