aboutsummaryrefslogtreecommitdiffstats
path: root/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
diff options
context:
space:
mode:
authorKai Uwe Broulik <kde@privat.broulik.de>2023-08-18 17:24:15 +0200
committerKai Uwe Broulik <kde@privat.broulik.de>2023-08-21 20:52:01 +0200
commit8c852c70c9cd415f852239395101c8b9884d5688 (patch)
treefabba6420f0a07e1e21f3c728821527172eab79c /tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
parent9f4aeeabb982cfc4306c9d350dbb68f64914fb32 (diff)
QQmlDMAbstractItemModelData: Guard against deletion during model write
When a `setData` call on a model results in our row being removed (e.g. when a proxy model filters out the row as a result of this change), an Instantiator would delete us and we'd try to emit a property change on garbage `this`. Amends commit 5db77fb406bb36a27de1ad2e3390720411c833f9which made Instantiator clean up its objects on a model change. Pick-to: 6.6 6.5 Change-Id: I5570453de588e32dc5e4235287a83565e5185aa2 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp')
-rw-r--r--tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp89
1 files changed, 89 insertions, 0 deletions
diff --git a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
index 1097c65f02..f6d2752889 100644
--- a/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
+++ b/tests/auto/qml/qqmlinstantiator/tst_qqmlinstantiator.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qtest.h>
#include <QSignalSpy>
+#include <QSortFilterProxyModel>
#include <QDebug>
#include <QtQml/qqmlengine.h>
@@ -28,6 +29,7 @@ private slots:
void activeModelChangeInteraction();
void intModelChange();
void createAndRemove();
+ void removeDuringModelChange();
void asynchronous_data();
void asynchronous();
@@ -306,6 +308,93 @@ void tst_qqmlinstantiator::boundDelegateComponent()
QCOMPARE(b->objectAt(2)->objectName(), QStringLiteral("root3"));
}
+class SingleBoolItemModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ SingleBoolItemModel(QObject *parent = nullptr) : QAbstractListModel(parent) {}
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override
+ {
+ if (parent.isValid())
+ return 0;
+ return 1;
+ }
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ if (index.parent().isValid() || index.row() != 0 || index.column() != 0
+ || role != Qt::UserRole)
+ return QVariant();
+
+ return m_active;
+ }
+
+ bool setData(const QModelIndex &index, const QVariant &value,
+ int role) override {
+ if (index.parent().isValid() || index.row() != 0 || index.column() != 0
+ || role != Qt::UserRole || m_active == value.toBool())
+ return false;
+
+ m_active = value.toBool();
+ Q_EMIT dataChanged(index, index, QList<int>{Qt::UserRole});
+ return true;
+ }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ return { {Qt::UserRole, "active"} };
+ }
+
+private:
+ bool m_active = true;
+};
+
+class FilterBoolRoleProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ FilterBoolRoleProxyModel(QObject *parent = nullptr)
+ : QSortFilterProxyModel(parent) {}
+
+ bool filterAcceptsRow(int source_row,
+ const QModelIndex &source_parent) const override
+ {
+ return sourceModel()->index(source_row, 0, source_parent).data(Qt::UserRole).toBool();
+ }
+};
+
+void tst_qqmlinstantiator::removeDuringModelChange()
+{
+ SingleBoolItemModel model;
+
+ FilterBoolRoleProxyModel proxyModel;
+ proxyModel.setSourceModel(&model);
+ proxyModel.setFilterRole(Qt::UserRole);
+ QCOMPARE(proxyModel.rowCount(), 1);
+
+ QQmlEngine engine;
+ const QUrl url(testFileUrl("removeDuringModelChange.qml"));
+ QQmlComponent component(&engine, url);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+
+ QScopedPointer<QObject> o(component.create());
+ QVERIFY2(!o.isNull(), qPrintable(component.errorString()));
+
+ QQmlInstantiator *instantiator = qobject_cast<QQmlInstantiator *>(o.data());
+
+ instantiator->setModel(QVariant::fromValue(&proxyModel));
+
+ QSignalSpy removedSpy(instantiator, &QQmlInstantiator::objectRemoved);
+ QMetaObject::invokeMethod(instantiator->object(), "deactivate");
+
+ // We should still be alive at this point.
+ QCOMPARE(removedSpy.size(), 1);
+ QCOMPARE(proxyModel.rowCount(), 0);
+}
+
QTEST_MAIN(tst_qqmlinstantiator)
#include "tst_qqmlinstantiator.moc"