qt5base-lts/tests/auto/testlib/qabstractitemmodeltester/tst_qabstractitemmodeltester.cpp
Marc Mutz 51edc438dd tst_QAbstractItemModelTester: fix mem-leak
QThreeWidgetItems that have been removed from their parents (or the
widget) must be deleted manually. The treeWidgetModel() test forgot
that, driving asan nuts.

Code predates the beginning of the public history, so picking to all
active branches.

Pick-to: 6.6 6.5 6.2 5.15
Change-Id: I139549b0bd8baf4abfb90f926f6290119471046f
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
2023-07-18 12:13:30 +02:00

294 lines
8.9 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QAbstractItemModelTester>
#include <QtGui/QtGui>
#include <QtWidgets/QtWidgets>
#include "dynamictreemodel.h"
class tst_QAbstractItemModelTester : public QObject
{
Q_OBJECT
private slots:
void stringListModel();
void treeWidgetModel();
void standardItemModel();
void standardItemModelZeroColumns();
void testInsertThroughProxy();
void moveSourceItems();
void testResetThroughProxy();
};
/*
tests
*/
void tst_QAbstractItemModelTester::stringListModel()
{
QStringListModel model;
QSortFilterProxyModel proxy;
QAbstractItemModelTester t1(&model);
QAbstractItemModelTester t2(&proxy);
proxy.setSourceModel(&model);
model.setStringList(QStringList() << "2" << "3" << "1");
model.setStringList(QStringList() << "a" << "e" << "plop" << "b" << "c");
proxy.setDynamicSortFilter(true);
proxy.setFilterRegularExpression(QRegularExpression("[^b]"));
}
void tst_QAbstractItemModelTester::treeWidgetModel()
{
QTreeWidget widget;
QAbstractItemModelTester t1(widget.model());
QTreeWidgetItem *root = new QTreeWidgetItem(&widget, QStringList("root"));
for (int i = 0; i < 20; ++i)
new QTreeWidgetItem(root, QStringList(QString::number(i)));
QTreeWidgetItem *remove = root->child(2);
root->removeChild(remove);
delete remove;
QTreeWidgetItem *parent = new QTreeWidgetItem(&widget, QStringList("parent"));
new QTreeWidgetItem(parent, QStringList("child"));
parent->setHidden(true);
widget.sortByColumn(0, Qt::AscendingOrder);
}
void tst_QAbstractItemModelTester::standardItemModel()
{
QStandardItemModel model(10, 10);
QSortFilterProxyModel proxy;
QAbstractItemModelTester t1(&model);
QAbstractItemModelTester t2(&proxy);
proxy.setSourceModel(&model);
model.insertRows(2, 5);
model.removeRows(4, 5);
model.insertColumns(2, 5);
model.removeColumns(4, 5);
model.insertRows(0, 5, model.index(1, 1));
model.insertColumns(0, 5, model.index(1, 3));
}
void tst_QAbstractItemModelTester::standardItemModelZeroColumns()
{
QStandardItemModel model;
QAbstractItemModelTester t1(&model);
// QTBUG-92220
model.insertRows(0, 5);
model.removeRows(0, 5);
// QTBUG-92886
model.insertRows(0, 5);
model.removeRows(1, 2);
const QModelIndex parentIndex = model.index(0, 0);
model.insertRows(0, 5, parentIndex);
model.removeRows(1, 2, parentIndex);
}
void tst_QAbstractItemModelTester::testInsertThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(model);
new QAbstractItemModelTester(proxy, this);
ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
// Parent is QModelIndex()
insertCommand->doCommand();
insertCommand = new ModelInsertCommand(model, this);
insertCommand->setNumCols(4);
insertCommand->setAncestorRowNumbers(QList<int>() << 5);
insertCommand->setStartRow(0);
insertCommand->setEndRow(9);
insertCommand->doCommand();
ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setNumCols(4);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestRow(9);
moveCommand->setDestAncestors(QList<int>() << 5);
moveCommand->doCommand();
}
/**
Makes the persistent index list publicly accessible
*/
class AccessibleProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
AccessibleProxyModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent)
{
}
QModelIndexList persistent()
{
return persistentIndexList();
}
};
class ObservingObject : public QObject
{
Q_OBJECT
public:
ObservingObject(AccessibleProxyModel *proxy, QObject *parent = nullptr) :
QObject(parent),
m_proxy(proxy),
storePersistentFailureCount(0),
checkPersistentFailureCount(0)
{
connect(m_proxy, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(storePersistent()));
connect(m_proxy, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
SLOT(checkPersistent()));
}
public slots:
void storePersistent(const QModelIndex &parent)
{
for (int row = 0; row < m_proxy->rowCount(parent); ++row) {
QModelIndex proxyIndex = m_proxy->index(row, 0, parent);
QModelIndex sourceIndex = m_proxy->mapToSource(proxyIndex);
if (!proxyIndex.isValid()) {
qWarning("%s: Invalid proxy index", Q_FUNC_INFO);
++storePersistentFailureCount;
}
if (!sourceIndex.isValid()) {
qWarning("%s: invalid source index", Q_FUNC_INFO);
++storePersistentFailureCount;
}
m_persistentSourceIndexes.append(sourceIndex);
m_persistentProxyIndexes.append(proxyIndex);
if (m_proxy->hasChildren(proxyIndex))
storePersistent(proxyIndex);
}
}
void storePersistent()
{
// This method is called from rowsAboutToBeMoved. Persistent indexes should be valid
foreach (const QModelIndex &idx, m_persistentProxyIndexes)
if (!idx.isValid()) {
qWarning("%s: persistentProxyIndexes contains invalid index", Q_FUNC_INFO);
++storePersistentFailureCount;
}
if (!m_proxy->persistent().isEmpty()) {
qWarning("%s: proxy should have no persistent indexes when storePersistent called",
Q_FUNC_INFO);
++storePersistentFailureCount;
}
storePersistent(QModelIndex());
if (m_proxy->persistent().isEmpty()) {
qWarning("%s: proxy should have persistent index after storePersistent called",
Q_FUNC_INFO);
++storePersistentFailureCount;
}
}
void checkPersistent()
{
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
m_persistentProxyIndexes.at(row);
m_persistentSourceIndexes.at(row);
}
for (int row = 0; row < m_persistentProxyIndexes.size(); ++row) {
QModelIndex updatedProxy = m_persistentProxyIndexes.at(row);
QModelIndex updatedSource = m_persistentSourceIndexes.at(row);
if (m_proxy->mapToSource(updatedProxy) != updatedSource) {
qWarning("%s: check failed at row %d", Q_FUNC_INFO, row);
++checkPersistentFailureCount;
}
}
m_persistentSourceIndexes.clear();
m_persistentProxyIndexes.clear();
}
private:
AccessibleProxyModel *m_proxy;
QList<QPersistentModelIndex> m_persistentSourceIndexes;
QList<QPersistentModelIndex> m_persistentProxyIndexes;
public:
int storePersistentFailureCount;
int checkPersistentFailureCount;
};
void tst_QAbstractItemModelTester::moveSourceItems()
{
DynamicTreeModel *model = new DynamicTreeModel(this);
AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);
ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();
insertCommand = new ModelInsertCommand(model, this);
insertCommand->setAncestorRowNumbers(QList<int>() << 1);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();
ObservingObject observer(proxy);
ModelMoveCommand *moveCommand = new ModelMoveCommand(model, this);
moveCommand->setStartRow(0);
moveCommand->setEndRow(0);
moveCommand->setDestAncestors(QList<int>() << 1);
moveCommand->setDestRow(0);
moveCommand->doCommand();
QCOMPARE(observer.storePersistentFailureCount, 0);
QCOMPARE(observer.checkPersistentFailureCount, 0);
}
void tst_QAbstractItemModelTester::testResetThroughProxy()
{
DynamicTreeModel *model = new DynamicTreeModel(this);
ModelInsertCommand *insertCommand = new ModelInsertCommand(model, this);
insertCommand->setStartRow(0);
insertCommand->setEndRow(2);
insertCommand->doCommand();
QPersistentModelIndex persistent = model->index(0, 0);
AccessibleProxyModel *proxy = new AccessibleProxyModel(this);
proxy->setSourceModel(model);
ObservingObject observer(proxy);
observer.storePersistent();
ModelResetCommand *resetCommand = new ModelResetCommand(model, this);
resetCommand->setNumCols(0);
resetCommand->doCommand();
QCOMPARE(observer.storePersistentFailureCount, 0);
QCOMPARE(observer.checkPersistentFailureCount, 0);
}
QTEST_MAIN(tst_QAbstractItemModelTester)
#include "tst_qabstractitemmodeltester.moc"