diff options
Diffstat (limited to 'tests/auto/corelib/itemmodels')
40 files changed, 2394 insertions, 1375 deletions
diff --git a/tests/auto/corelib/itemmodels/CMakeLists.txt b/tests/auto/corelib/itemmodels/CMakeLists.txt new file mode 100644 index 0000000000..a8aa743887 --- /dev/null +++ b/tests/auto/corelib/itemmodels/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +add_subdirectory(qstringlistmodel) +if(TARGET Qt::Gui) + add_subdirectory(qabstractitemmodel) + if(QT_FEATURE_proxymodel) + if (NOT WASM) # QTBUG-121822 + add_subdirectory(qabstractproxymodel) + endif() + add_subdirectory(qconcatenatetablesproxymodel) + add_subdirectory(qidentityproxymodel) + add_subdirectory(qsortfilterproxymodel_recursive) + add_subdirectory(qsortfilterproxymodel_regularexpression) + add_subdirectory(qtransposeproxymodel) + endif() + add_subdirectory(qitemselectionmodel) +endif() +if(TARGET Qt::Widgets AND QT_FEATURE_proxymodel) + add_subdirectory(qsortfilterproxymodel) +endif() +if(TARGET Qt::Sql AND TARGET Qt::Widgets) + add_subdirectory(qitemmodel) +endif() diff --git a/tests/auto/corelib/itemmodels/itemmodels.pro b/tests/auto/corelib/itemmodels/itemmodels.pro deleted file mode 100644 index ffbda6ec40..0000000000 --- a/tests/auto/corelib/itemmodels/itemmodels.pro +++ /dev/null @@ -1,21 +0,0 @@ -TEMPLATE=subdirs - -SUBDIRS = qstringlistmodel - -qtHaveModule(gui): SUBDIRS += \ - qabstractitemmodel \ - qabstractproxymodel \ - qconcatenatetablesproxymodel \ - qidentityproxymodel \ - qitemselectionmodel \ - qsortfilterproxymodel_recursive \ - qtransposeproxymodel \ - -qtHaveModule(widgets) { - SUBDIRS += \ - qsortfilterproxymodel_regexp \ - qsortfilterproxymodel_regularexpression - - qtHaveModule(sql): SUBDIRS += \ - qitemmodel -} diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qabstractitemmodel/CMakeLists.txt new file mode 100644 index 0000000000..e5c7c08fc8 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qabstractitemmodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractitemmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qabstractitemmodel + SOURCES + ../../../other/qabstractitemmodelutils/dynamictreemodel.cpp ../../../other/qabstractitemmodelutils/dynamictreemodel.h + tst_qabstractitemmodel.cpp + INCLUDE_DIRECTORIES + ../../../other/qabstractitemmodelutils + LIBRARIES + Qt::Gui + Qt::TestPrivate +) diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro b/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro deleted file mode 100644 index da1f87e76a..0000000000 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/qabstractitemmodel.pro +++ /dev/null @@ -1,9 +0,0 @@ -CONFIG += testcase -TARGET = tst_qabstractitemmodel -QT = core testlib gui - -mtdir = ../../../other/qabstractitemmodelutils -INCLUDEPATH += $$PWD/$${mtdir} -SOURCES = tst_qabstractitemmodel.cpp $${mtdir}/dynamictreemodel.cpp -HEADERS = $${mtdir}/dynamictreemodel.h -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp index 9fab36deaa..c48b79b260 100644 --- a/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractitemmodel/tst_qabstractitemmodel.cpp @@ -1,40 +1,29 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> -#include <QtCore/QCoreApplication> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> + +#include <QtCore/QCoreApplication> +#if QT_CONFIG(sortfilterproxymodel) #include <QtCore/QSortFilterProxyModel> +#endif #include <QtCore/QStringListModel> #include <QtGui/QStandardItemModel> #include "dynamictreemodel.h" +// for testing QModelRoleDataSpan construction +#include <QVarLengthArray> +#include <QSignalSpy> +#include <QMimeData> + +#include <array> +#include <vector> +#include <deque> +#include <list> + /*! Note that this doesn't test models, but any functionality that QAbstractItemModel should provide */ @@ -71,6 +60,7 @@ private slots: void reset(); void complexChangesWithPersistent(); + void modelIndexComparisons(); void testMoveSameParentUp_data(); void testMoveSameParentUp(); @@ -92,9 +82,11 @@ private slots: void testMoveWithinOwnRange_data(); void testMoveWithinOwnRange(); +#if QT_CONFIG(sortfilterproxymodel) void testMoveThroughProxy(); void testReset(); +#endif void testDataChanged(); @@ -108,6 +100,10 @@ private slots: void checkIndex(); + void modelRoleDataSpanConstruction(); + void modelRoleDataSpan(); + + void multiData(); private: DynamicTreeModel *m_model; }; @@ -121,32 +117,32 @@ private: class QtTestModel: public QAbstractItemModel { public: - QtTestModel(int rows, int columns, QObject *parent = 0); - QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent = 0); - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &) const; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - bool hasChildren(const QModelIndex &) const; - QVariant data(const QModelIndex &idx, int) const; - bool setData(const QModelIndex &idx, const QVariant &value, int); - bool insertRows(int row, int count, const QModelIndex &parent= QModelIndex()); - bool insertColumns(int column, int count, const QModelIndex &parent= QModelIndex()); + QtTestModel(int rows, int columns, QObject *parent = nullptr); + QtTestModel(const QList<QList<QString> > tbl, QObject *parent = nullptr); + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + bool hasChildren(const QModelIndex &) const override; + QVariant data(const QModelIndex &idx, int) const override; + bool setData(const QModelIndex &idx, const QVariant &value, int) override; + bool insertRows(int row, int count, const QModelIndex &parent= QModelIndex()) override; + bool insertColumns(int column, int count, const QModelIndex &parent= QModelIndex()) override; void setPersistent(const QModelIndex &from, const QModelIndex &to); - bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex() ); - bool removeColumns( int column, int count, const QModelIndex & parent = QModelIndex()); + bool removeRows ( int row, int count, const QModelIndex & parent = QModelIndex()) override; + bool removeColumns( int column, int count, const QModelIndex & parent = QModelIndex()) override; bool moveRows (const QModelIndex &sourceParent, int sourceRow, int count, - const QModelIndex &destinationParent, int destinationChild); + const QModelIndex &destinationParent, int destinationChild) override; bool moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, - const QModelIndex &destinationParent, int destinationChild); + const QModelIndex &destinationParent, int destinationChild) override; void reset(); bool canDropMimeData(const QMimeData *data, Qt::DropAction action, - int row, int column, const QModelIndex &parent) const; + int row, int column, const QModelIndex &parent) const override; int cCount, rCount; mutable bool wrongIndex; - QVector<QVector<QString> > table; + QList<QList<QString> > table; }; Q_DECLARE_METATYPE(QAbstractItemModel::LayoutChangeHint); @@ -163,12 +159,12 @@ QtTestModel::QtTestModel(int rows, int columns, QObject *parent) } } -QtTestModel::QtTestModel(const QVector<QVector<QString> > tbl, QObject *parent) +QtTestModel::QtTestModel(const QList<QList<QString> > tbl, QObject *parent) : QAbstractItemModel(parent), wrongIndex(false) { table = tbl; - rCount = tbl.count(); - cCount = tbl.at(0).count(); + rCount = tbl.size(); + cCount = tbl.at(0).size(); } QModelIndex QtTestModel::index(int row, int column, const QModelIndex &parent) const @@ -201,8 +197,8 @@ bool QtTestModel::insertRows(int row, int count, const QModelIndex &parent) { QAbstractItemModel::beginInsertRows(parent, row, row + count - 1); int cc = columnCount(parent); - table.insert(row, count, QVector<QString>(cc)); - rCount = table.count(); + table.insert(row, count, QList<QString>(cc)); + rCount = table.size(); QAbstractItemModel::endInsertRows(); return true; } @@ -213,7 +209,7 @@ bool QtTestModel::insertColumns(int column, int count, const QModelIndex &parent int rc = rowCount(parent); for (int i = 0; i < rc; ++i) table[i].insert(column, 1, ""); - cCount = table.at(0).count(); + cCount = table.at(0).size(); QAbstractItemModel::endInsertColumns(); return true; } @@ -229,7 +225,7 @@ bool QtTestModel::removeRows( int row, int count, const QModelIndex & parent) for (int r = row+count-1; r >= row; --r) table.remove(r); - rCount = table.count(); + rCount = table.size(); QAbstractItemModel::endRemoveRows(); return true; @@ -243,7 +239,7 @@ bool QtTestModel::removeColumns(int column, int count, const QModelIndex & paren for (int r = 0; r < rCount; ++r) table[r].remove(c); - cCount = table.at(0).count(); + cCount = table.at(0).size(); QAbstractItemModel::endRemoveColumns(); return true; @@ -256,7 +252,7 @@ bool QtTestModel::moveRows(const QModelIndex &sourceParent, int src, int cnt, destinationParent, dst)) return false; - QVector<QString> buf; + QList<QString> buf; if (dst < src) { for (int i = 0; i < cnt; ++i) { buf.swap(table[src + i]); @@ -271,7 +267,7 @@ bool QtTestModel::moveRows(const QModelIndex &sourceParent, int src, int cnt, } } - rCount = table.count(); + rCount = table.size(); QAbstractItemModel::endMoveRows(); return true; @@ -301,7 +297,7 @@ bool QtTestModel::moveColumns(const QModelIndex &sourceParent, int src, int cnt, } } - cCount = table.at(0).count(); + cCount = table.at(0).size(); QAbstractItemModel::endMoveColumns(); return true; @@ -425,11 +421,11 @@ void tst_QAbstractItemModel::itemFlags() void tst_QAbstractItemModel::match() { - QtTestModel model(4, 1); + QtTestModel model(5, 1); QModelIndex start = model.index(0, 0, QModelIndex()); QVERIFY(start.isValid()); QModelIndexList res = model.match(start, Qt::DisplayRole, QVariant("1"), 3); - QCOMPARE(res.count(), 1); + QCOMPARE(res.size(), 1); QModelIndex idx = model.index(1, 0, QModelIndex()); bool areEqual = (idx == res.first()); QVERIFY(areEqual); @@ -438,44 +434,45 @@ void tst_QAbstractItemModel::match() model.setData(model.index(1, 0, QModelIndex()), "cat", Qt::DisplayRole); model.setData(model.index(2, 0, QModelIndex()), "dog", Qt::DisplayRole); model.setData(model.index(3, 0, QModelIndex()), "boar", Qt::DisplayRole); + model.setData(model.index(4, 0, QModelIndex()), "bo/a/r", Qt::DisplayRole); // QTBUG-104585 res = model.match(start, Qt::DisplayRole, QVariant("dog"), -1, Qt::MatchExactly); - QCOMPARE(res.count(), 1); + QCOMPARE(res.size(), 1); res = model.match(start, Qt::DisplayRole, QVariant("a"), -1, Qt::MatchContains); - QCOMPARE(res.count(), 3); + QCOMPARE(res.size(), 4); res = model.match(start, Qt::DisplayRole, QVariant("b"), -1, Qt::MatchStartsWith); - QCOMPARE(res.count(), 2); + QCOMPARE(res.size(), 3); res = model.match(start, Qt::DisplayRole, QVariant("t"), -1, Qt::MatchEndsWith); - QCOMPARE(res.count(), 2); + QCOMPARE(res.size(), 2); res = model.match(start, Qt::DisplayRole, QVariant("*a*"), -1, Qt::MatchWildcard); - QCOMPARE(res.count(), 3); - res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegExp); - QCOMPARE(res.count(), 2); - res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegExp | Qt::MatchCaseSensitive); - QCOMPARE(res.count(), 0); + QCOMPARE(res.size(), 4); + res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegularExpression); + QCOMPARE(res.size(), 3); + res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegularExpression | Qt::MatchCaseSensitive); + QCOMPARE(res.size(), 0); res = model.match(start, Qt::DisplayRole, QVariant("BOAR"), -1, Qt::MatchFixedString); - QCOMPARE(res.count(), 1); + QCOMPARE(res.size(), 1); res = model.match(start, Qt::DisplayRole, QVariant("bat"), -1, Qt::MatchFixedString | Qt::MatchCaseSensitive); - QCOMPARE(res.count(), 1); + QCOMPARE(res.size(), 1); res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegularExpression); - QCOMPARE(res.count(), 2); + QCOMPARE(res.size(), 3); res = model.match(start, Qt::DisplayRole, QVariant(".*O.*"), -1, Qt::MatchRegularExpression | Qt::MatchCaseSensitive); - QCOMPARE(res.count(), 0); + QCOMPARE(res.size(), 0); res = model.match(start, Qt::DisplayRole, QVariant(QRegularExpression(".*O.*")), -1, Qt::MatchRegularExpression); - QCOMPARE(res.count(), 0); + QCOMPARE(res.size(), 0); res = model.match(start, Qt::DisplayRole, QVariant(QRegularExpression(".*O.*", QRegularExpression::CaseInsensitiveOption)), -1, Qt::MatchRegularExpression); - QCOMPARE(res.count(), 2); + QCOMPARE(res.size(), 3); // Ensure that the case sensitivity is properly ignored when passing a // QRegularExpression object. @@ -485,13 +482,13 @@ void tst_QAbstractItemModel::match() QRegularExpression::CaseInsensitiveOption)), -1, Qt::MatchRegularExpression | Qt::MatchCaseSensitive); - QCOMPARE(res.count(), 2); + QCOMPARE(res.size(), 3); } typedef QPair<int, int> Position; -typedef QVector<QPair<int, int> > Selection; -typedef QVector<QVector<QString> > StringTable; -typedef QVector<QString> StringTableRow; +typedef QList<QPair<int, int> > Selection; +typedef QList<QList<QString> > StringTable; +typedef QList<QString> StringTableRow; static StringTableRow qStringTableRow(const QString &s1, const QString &s2, const QString &s3) { @@ -768,7 +765,7 @@ void tst_QAbstractItemModel::dropMimeData() // get the mimeData from the "selected" indexes QModelIndexList selectedIndexes; - for (int i = 0; i < selection.count(); ++i) + for (int i = 0; i < selection.size(); ++i) selectedIndexes << src.index(selection.at(i).first, selection.at(i).second, QModelIndex()); QMimeData *md = src.mimeData(selectedIndexes); // do the drop @@ -839,8 +836,8 @@ void tst_QAbstractItemModel::removeRows() QVERIFY(rowsRemovedSpy.isValid()); QCOMPARE(model.removeRows(6, 4), true); - QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1); - QCOMPARE(rowsRemovedSpy.count(), 1); + QCOMPARE(rowsAboutToBeRemovedSpy.size(), 1); + QCOMPARE(rowsRemovedSpy.size(), 1); } void tst_QAbstractItemModel::removeColumns() @@ -854,8 +851,8 @@ void tst_QAbstractItemModel::removeColumns() QVERIFY(columnsRemovedSpy.isValid()); QCOMPARE(model.removeColumns(6, 4), true); - QCOMPARE(columnsAboutToBeRemovedSpy.count(), 1); - QCOMPARE(columnsRemovedSpy.count(), 1); + QCOMPARE(columnsAboutToBeRemovedSpy.size(), 1); + QCOMPARE(columnsRemovedSpy.size(), 1); } void tst_QAbstractItemModel::insertRows() @@ -869,8 +866,8 @@ void tst_QAbstractItemModel::insertRows() QVERIFY(rowsInsertedSpy.isValid()); QCOMPARE(model.insertRows(6, 4), true); - QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1); - QCOMPARE(rowsInsertedSpy.count(), 1); + QCOMPARE(rowsAboutToBeInsertedSpy.size(), 1); + QCOMPARE(rowsInsertedSpy.size(), 1); } void tst_QAbstractItemModel::insertColumns() @@ -884,8 +881,8 @@ void tst_QAbstractItemModel::insertColumns() QVERIFY(columnsInsertedSpy.isValid()); QCOMPARE(model.insertColumns(6, 4), true); - QCOMPARE(columnsAboutToBeInsertedSpy.count(), 1); - QCOMPARE(columnsInsertedSpy.count(), 1); + QCOMPARE(columnsAboutToBeInsertedSpy.size(), 1); + QCOMPARE(columnsInsertedSpy.size(), 1); } void tst_QAbstractItemModel::moveRows() @@ -899,8 +896,8 @@ void tst_QAbstractItemModel::moveRows() QVERIFY(rowsMovedSpy.isValid()); QCOMPARE(model.moveRows(QModelIndex(), 6, 4, QModelIndex(), 1), true); - QCOMPARE(rowsAboutToBeMovedSpy.count(), 1); - QCOMPARE(rowsMovedSpy.count(), 1); + QCOMPARE(rowsAboutToBeMovedSpy.size(), 1); + QCOMPARE(rowsMovedSpy.size(), 1); } void tst_QAbstractItemModel::moveColumns() @@ -914,12 +911,12 @@ void tst_QAbstractItemModel::moveColumns() QVERIFY(columnsMovedSpy.isValid()); QCOMPARE(model.moveColumns(QModelIndex(), 6, 4, QModelIndex(), 1), true); - QCOMPARE(columnsAboutToBeMovedSpy.count(), 1); - QCOMPARE(columnsMovedSpy.count(), 1); + QCOMPARE(columnsAboutToBeMovedSpy.size(), 1); + QCOMPARE(columnsMovedSpy.size(), 1); QCOMPARE(model.moveColumn(QModelIndex(), 4, QModelIndex(), 1), true); - QCOMPARE(columnsAboutToBeMovedSpy.count(), 2); - QCOMPARE(columnsMovedSpy.count(), 2); + QCOMPARE(columnsAboutToBeMovedSpy.size(), 2); + QCOMPARE(columnsMovedSpy.size(), 2); } void tst_QAbstractItemModel::reset() @@ -929,7 +926,7 @@ void tst_QAbstractItemModel::reset() QSignalSpy resetSpy(&model, &QtTestModel::modelReset); QVERIFY(resetSpy.isValid()); model.reset(); - QCOMPARE(resetSpy.count(), 1); + QCOMPARE(resetSpy.size(), 1); } void tst_QAbstractItemModel::complexChangesWithPersistent() @@ -994,6 +991,42 @@ void tst_QAbstractItemModel::complexChangesWithPersistent() QVERIFY(e[i] == model.index(2, i-2 , QModelIndex())); } +void tst_QAbstractItemModel::modelIndexComparisons() +{ + QTestPrivate::testAllComparisonOperatorsCompile<QModelIndex>(); + QTestPrivate::testAllComparisonOperatorsCompile<QPersistentModelIndex>(); + QTestPrivate::testAllComparisonOperatorsCompile<QPersistentModelIndex, QModelIndex>(); + + QtTestModel model(3, 3); + + QModelIndex mi11 = model.index(1, 1); + QModelIndex mi22 = model.index(2, 2); + QPersistentModelIndex pmi11 = mi11; + QPersistentModelIndex pmi22 = mi22; + QPersistentModelIndex pmiU; + + QT_TEST_EQUALITY_OPS(mi11, mi11, true); + QT_TEST_EQUALITY_OPS(mi11, mi22, false); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(mi11, mi22, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(mi22, mi11, Qt::strong_ordering::greater); + QT_TEST_EQUALITY_OPS(pmi11, pmi11, true); + QT_TEST_EQUALITY_OPS(pmi11, pmi22, false); + QT_TEST_EQUALITY_OPS(pmi11, mi11, true); + QT_TEST_EQUALITY_OPS(pmi11, mi22, false); + + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmi22, Qt::strong_ordering::less); + // Disengaged QPMIs are sorted randomly (based on address of their Private) + // So all we can check here is QPMIs with d == nullptr, which should reliably + // come before any others. + QT_TEST_ALL_COMPARISON_OPS(pmiU, pmiU, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, pmiU, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(pmi11, mi11, Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(pmi11, mi22, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(pmiU, mi11, Qt::strong_ordering::less); +} + void tst_QAbstractItemModel::testMoveSameParentDown_data() { QTest::addColumn<int>("startRow"); @@ -1225,6 +1258,7 @@ void tst_QAbstractItemModel::testMoveSameParentUp() } } +#if QT_CONFIG(sortfilterproxymodel) void tst_QAbstractItemModel::testMoveThroughProxy() { QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this); @@ -1243,6 +1277,7 @@ void tst_QAbstractItemModel::testMoveThroughProxy() moveCommand->setDestRow(0); moveCommand->doCommand(); } +#endif void tst_QAbstractItemModel::testMoveToGrandParent_data() { @@ -1790,6 +1825,7 @@ void tst_QAbstractItemModel::testMoveWithinOwnRange() QCOMPARE(afterSpy.size(), 0); } +#if QT_CONFIG(proxymodel) class ListenerObject : public QObject { Q_OBJECT @@ -1808,7 +1844,7 @@ private: QList<QPersistentModelIndex> m_persistentIndexes; QModelIndexList m_nonPersistentIndexes; }; - +#endif class ModelWithCustomRole : public QStringListModel { @@ -1822,6 +1858,7 @@ public: } }; +#if QT_CONFIG(proxymodel) ListenerObject::ListenerObject(QAbstractProxyModel *parent) : QObject(parent), m_model(parent) { @@ -1858,11 +1895,13 @@ void ListenerObject::slotAboutToBeReset() void ListenerObject::slotReset() { - for (const auto &idx : qAsConst(m_persistentIndexes)) { + for (const auto &idx : std::as_const(m_persistentIndexes)) { QVERIFY(!idx.isValid()); } } +#endif +#if QT_CONFIG(sortfilterproxymodel) void tst_QAbstractItemModel::testReset() { QSignalSpy beforeResetSpy(m_model, &DynamicTreeModel::modelAboutToBeReset); @@ -1917,6 +1956,7 @@ void tst_QAbstractItemModel::testReset() // After being reset the proxy must be queried again. QCOMPARE(nullProxy->roleNames().value(Qt::UserRole + 1), QByteArray()); } +#endif class CustomRoleModel : public QStringListModel { @@ -1929,7 +1969,7 @@ public: UserRole }; - CustomRoleModel(QObject *parent = 0) + CustomRoleModel(QObject *parent = nullptr) : QStringListModel(QStringList() << "a" << "b" << "c", parent) { } @@ -1940,8 +1980,8 @@ public: const QModelIndex bottom = index(2, 0); emit dataChanged(top, bottom); - emit dataChanged(top, bottom, QVector<int>() << Qt::ToolTipRole); - emit dataChanged(top, bottom, QVector<int>() << Qt::ToolTipRole << Custom1); + emit dataChanged(top, bottom, QList<int>() << Qt::ToolTipRole); + emit dataChanged(top, bottom, QList<int>() << Qt::ToolTipRole << Custom1); } }; @@ -1963,8 +2003,8 @@ void tst_QAbstractItemModel::testDataChanged() const QVariantList secondEmission = withRoles.at(1); const QVariantList thirdEmission = withRoles.at(2); - const QVector<int> secondRoles = secondEmission.at(2).value<QVector<int> >(); - const QVector<int> thirdRoles = thirdEmission.at(2).value<QVector<int> >(); + const QList<int> secondRoles = secondEmission.at(2).value<QList<int> >(); + const QList<int> thirdRoles = thirdEmission.at(2).value<QList<int> >(); QCOMPARE(secondRoles.size(), 1); QVERIFY(secondRoles.contains(Qt::ToolTipRole)); @@ -1980,7 +2020,7 @@ class SignalArgumentChecker : public QObject { Q_OBJECT public: - SignalArgumentChecker(const QModelIndex &p1, const QModelIndex &p2, QObject *parent = 0) + SignalArgumentChecker(const QModelIndex &p1, const QModelIndex &p2, QObject *parent = nullptr) : QObject(parent), m_p1(p1), m_p2(p2), m_p1Persistent(p1), m_p2Persistent(p2) { connect(p1.model(), SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>)), SLOT(layoutAboutToBeChanged(QList<QPersistentModelIndex>))); @@ -2154,20 +2194,20 @@ class OverrideRoleNamesAndDragActions : public QStringListModel { Q_OBJECT public: - OverrideRoleNamesAndDragActions(QObject *parent = 0) + OverrideRoleNamesAndDragActions(QObject *parent = nullptr) : QStringListModel(parent) { } - QHash<int, QByteArray> roleNames() const + QHash<int, QByteArray> roleNames() const override { QHash<int, QByteArray> roles = QStringListModel::roleNames(); roles.insert(Qt::UserRole + 2, "custom"); return roles; } - Qt::DropActions supportedDragActions() const + Qt::DropActions supportedDragActions() const override { return QStringListModel::supportedDragActions() | Qt::MoveAction; } @@ -2193,7 +2233,7 @@ class OverrideDropActions : public QStringListModel { Q_OBJECT public: - OverrideDropActions(QObject *parent = 0) + OverrideDropActions(QObject *parent = nullptr) : QStringListModel(parent) { } @@ -2214,7 +2254,7 @@ class SignalConnectionTester : public QObject { Q_OBJECT public: - SignalConnectionTester(QObject *parent = 0) + SignalConnectionTester(QObject *parent = nullptr) : QObject(parent), testPassed(false) { @@ -2384,5 +2424,135 @@ void tst_QAbstractItemModel::checkIndex() QVERIFY(!model.checkIndex(topLevelIndex, QAbstractItemModel::CheckIndexOption::IndexIsValid)); } +template <typename T> +inline constexpr bool CanConvertToSpan = std::is_convertible_v<T, QModelRoleDataSpan>; + +void tst_QAbstractItemModel::modelRoleDataSpanConstruction() +{ + // Compile time test + static_assert(CanConvertToSpan<QModelRoleData &>); + static_assert(CanConvertToSpan<QModelRoleData (&)[123]>); + static_assert(CanConvertToSpan<QVector<QModelRoleData> &>); + static_assert(CanConvertToSpan<QVarLengthArray<QModelRoleData> &>); + static_assert(CanConvertToSpan<std::vector<QModelRoleData> &>); + static_assert(CanConvertToSpan<std::array<QModelRoleData, 123> &>); + + static_assert(!CanConvertToSpan<QModelRoleData>); + static_assert(!CanConvertToSpan<QVector<QModelRoleData>>); + static_assert(!CanConvertToSpan<const QVector<QModelRoleData> &>); + static_assert(!CanConvertToSpan<std::vector<QModelRoleData>>); + static_assert(!CanConvertToSpan<std::deque<QModelRoleData>>); + static_assert(!CanConvertToSpan<std::deque<QModelRoleData> &>); + static_assert(!CanConvertToSpan<std::list<QModelRoleData> &>); + static_assert(!CanConvertToSpan<std::list<QModelRoleData>>); +} + +void tst_QAbstractItemModel::modelRoleDataSpan() +{ + QModelRoleData data[3] = { + QModelRoleData(Qt::DisplayRole), + QModelRoleData(Qt::DecorationRole), + QModelRoleData(Qt::EditRole) + }; + QModelRoleData *dataPtr = data; + + QModelRoleDataSpan span(data); + + QCOMPARE(span.size(), 3); + QCOMPARE(span.length(), 3); + QCOMPARE(span.data(), dataPtr); + QCOMPARE(span.begin(), dataPtr); + QCOMPARE(span.end(), dataPtr + 3); + for (int i = 0; i < 3; ++i) + QCOMPARE(span[i].role(), data[i].role()); + + data[0].setData(42); + data[1].setData(QStringLiteral("a string")); + data[2].setData(123.5); + + QCOMPARE(span.dataForRole(Qt::DisplayRole)->toInt(), 42); + QCOMPARE(span.dataForRole(Qt::DecorationRole)->toString(), "a string"); + QCOMPARE(span.dataForRole(Qt::EditRole)->toDouble(), 123.5); +} + +// model implementing data(), but not multiData(); check that the +// default implementation of multiData() does the right thing +class NonMultiDataRoleModel : public QAbstractListModel +{ + Q_OBJECT + +public: + int rowCount(const QModelIndex &) const override + { + return 1000; + } + + // We handle roles <= 10. All such roles return a QVariant(int) containing + // the same value as the role, except for 10 which returns a string. + QVariant data(const QModelIndex &index, int role) const override + { + Q_ASSERT(checkIndex(index, CheckIndexOption::IndexIsValid)); + + if (role < 10) + return QVariant::fromValue(role); + else if (role == 10) + return QVariant::fromValue(QStringLiteral("Hello!")); + + return QVariant(); + } +}; + +void tst_QAbstractItemModel::multiData() +{ + QModelRoleData data[] = { + QModelRoleData(1), + QModelRoleData(42), + QModelRoleData(5), + QModelRoleData(2), + QModelRoleData(12), + QModelRoleData(2), + QModelRoleData(10), + QModelRoleData(-123) + }; + + QModelRoleDataSpan span(data); + + for (const auto &roledata : span) + QVERIFY(roledata.data().isNull()); + + NonMultiDataRoleModel model; + const QModelIndex index = model.index(0, 0); + QVERIFY(index.isValid()); + + const auto check = [&]() { + for (auto &roledata : span) { + const auto role = roledata.role(); + if (role < 10) { + QVERIFY(!roledata.data().isNull()); + QVERIFY(roledata.data().userType() == qMetaTypeId<int>()); + QCOMPARE(roledata.data().toInt(), role); + } else if (role == 10) { + QVERIFY(!roledata.data().isNull()); + QVERIFY(roledata.data().userType() == qMetaTypeId<QString>()); + QCOMPARE(roledata.data().toString(), QStringLiteral("Hello!")); + } else { + QVERIFY(roledata.data().isNull()); + } + } + }; + + model.multiData(index, span); + check(); + + model.multiData(index, span); + check(); + + for (auto &roledata : span) + roledata.clearData(); + + model.multiData(index, span); + check(); +} + QTEST_MAIN(tst_QAbstractItemModel) #include "tst_qabstractitemmodel.moc" diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qabstractproxymodel/CMakeLists.txt new file mode 100644 index 0000000000..ea90d1dd79 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qabstractproxymodel/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qabstractproxymodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractproxymodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qabstractproxymodel + SOURCES + tst_qabstractproxymodel.cpp + NO_BATCH # QTBUG-121815 + DEFINES + QTEST_THROW_ON_FAIL + QTEST_THROW_ON_SKIP + LIBRARIES + Qt::Gui + Qt::TestPrivate +) diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro b/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro deleted file mode 100644 index 19aec43072..0000000000 --- a/tests/auto/corelib/itemmodels/qabstractproxymodel/qabstractproxymodel.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qabstractproxymodel -QT += testlib -SOURCES += tst_qabstractproxymodel.cpp -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp b/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp index 4406d40986..73a81f56ab 100644 --- a/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qabstractproxymodel/tst_qabstractproxymodel.cpp @@ -1,36 +1,19 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qpropertytesthelper_p.h> + +#ifndef QTEST_THROW_ON_FAIL +# error This test requires QTEST_THROW_ON_FAIL being active. +#endif + #include <qabstractproxymodel.h> #include <QItemSelection> #include <qstandarditemmodel.h> +#include <QtCore/qscopeguard.h> + class tst_QAbstractProxyModel : public QObject { Q_OBJECT @@ -43,6 +26,7 @@ private slots: void flags(); void headerData_data(); void headerData(); + void headerDataInBounds(); void itemData_data(); void itemData(); void mapFromSource_data(); @@ -60,6 +44,7 @@ private slots: void testRoleNames(); void testSwappingRowsProxy(); void testDragAndDrop(); + void sourceModelBinding(); }; // Subclass that exposes the protected functions. @@ -67,29 +52,29 @@ class SubQAbstractProxyModel : public QAbstractProxyModel { public: // QAbstractProxyModel::mapFromSource is a pure virtual function. - QModelIndex mapFromSource(QModelIndex const& sourceIndex) const + QModelIndex mapFromSource(QModelIndex const& sourceIndex) const override { Q_UNUSED(sourceIndex); return QModelIndex(); } // QAbstractProxyModel::mapToSource is a pure virtual function. - QModelIndex mapToSource(QModelIndex const& proxyIndex) const + QModelIndex mapToSource(QModelIndex const& proxyIndex) const override { Q_UNUSED(proxyIndex); return QModelIndex(); } - QModelIndex index(int, int, const QModelIndex&) const + QModelIndex index(int, int, const QModelIndex&) const override { return QModelIndex(); } - QModelIndex parent(const QModelIndex&) const + QModelIndex parent(const QModelIndex&) const override { return QModelIndex(); } - int rowCount(const QModelIndex&) const + int rowCount(const QModelIndex&) const override { return 0; } - int columnCount(const QModelIndex&) const + int columnCount(const QModelIndex&) const override { return 0; } @@ -172,6 +157,133 @@ void tst_QAbstractProxyModel::headerData() QCOMPARE(model.headerData(section, orientation, role), headerData); } +class SimpleTableReverseColumnsProxy : public QAbstractProxyModel +{ +public: + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return {}; + + if (row < 0 || row >= rowCount() || column < 0 || column >= columnCount()) + qFatal("error"); // cannot QFAIL here + + return createIndex(row, column); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return 0; + return sourceModel()->rowCount(); + } + + int columnCount(const QModelIndex &parent = QModelIndex()) const override + { + if (parent.isValid()) + return 0; + return sourceModel()->columnCount(); + } + + QModelIndex parent(const QModelIndex &) const override + { + return QModelIndex(); + } + + QModelIndex mapToSource(const QModelIndex &idx) const override + { + if (!idx.isValid()) + return QModelIndex(); + return sourceModel()->index(idx.row(), columnCount() - 1 - idx.column()); + } + + QModelIndex mapFromSource(const QModelIndex &idx) const override + { + if (idx.parent().isValid()) + return QModelIndex(); + return createIndex(idx.row(), columnCount() - 1 - idx.column()); + } +}; + +void tst_QAbstractProxyModel::headerDataInBounds() +{ + QStandardItemModel qsim(0, 5); + qsim.setHorizontalHeaderLabels({"Col1", "Col2", "Col3", "Col4", "Col5"}); + + SimpleTableReverseColumnsProxy proxy; + QSignalSpy headerDataChangedSpy(&proxy, &QAbstractItemModel::headerDataChanged); + QVERIFY(headerDataChangedSpy.isValid()); + proxy.setSourceModel(&qsim); + QCOMPARE(proxy.rowCount(), 0); + QCOMPARE(proxy.columnCount(), 5); + + for (int i = 0; i < proxy.columnCount(); ++i) { + QString expected = QString("Col%1").arg(i + 1); + QCOMPARE(proxy.headerData(i, Qt::Horizontal).toString(), expected); + } + + qsim.appendRow({ + new QStandardItem("A"), + new QStandardItem("B"), + new QStandardItem("C"), + new QStandardItem("D"), + new QStandardItem("E") + }); + + QCOMPARE(proxy.rowCount(), 1); + QCOMPARE(proxy.columnCount(), 5); + QTRY_COMPARE(headerDataChangedSpy.size(), 1); + QCOMPARE(headerDataChangedSpy[0][0].value<Qt::Orientation>(), Qt::Horizontal); + QCOMPARE(headerDataChangedSpy[0][1].value<int>(), 0); + QCOMPARE(headerDataChangedSpy[0][2].value<int>(), 4); + + for (int i = 0; i < proxy.columnCount(); ++i) { + QString expected = QString("Col%1").arg(proxy.columnCount() - i); + QCOMPARE(proxy.headerData(i, Qt::Horizontal).toString(), expected); + } + + qsim.appendRow({ + new QStandardItem("A"), + new QStandardItem("B"), + new QStandardItem("C"), + new QStandardItem("D"), + new QStandardItem("E") + }); + QCOMPARE(proxy.rowCount(), 2); + QCOMPARE(proxy.columnCount(), 5); + QCOMPARE(headerDataChangedSpy.size(), 1); + + for (int i = 0; i < proxy.columnCount(); ++i) { + QString expected = QString("Col%1").arg(proxy.columnCount() - i); + QCOMPARE(proxy.headerData(i, Qt::Horizontal).toString(), expected); + } + + QVERIFY(qsim.removeRows(0, 1)); + + QCOMPARE(proxy.rowCount(), 1); + QCOMPARE(proxy.columnCount(), 5); + QCOMPARE(headerDataChangedSpy.size(), 1); + + for (int i = 0; i < proxy.columnCount(); ++i) { + QString expected = QString("Col%1").arg(proxy.columnCount() - i); + QCOMPARE(proxy.headerData(i, Qt::Horizontal).toString(), expected); + } + + QVERIFY(qsim.removeRows(0, 1)); + + QCOMPARE(proxy.rowCount(), 0); + QCOMPARE(proxy.columnCount(), 5); + QTRY_COMPARE(headerDataChangedSpy.size(), 2); + QCOMPARE(headerDataChangedSpy[1][0].value<Qt::Orientation>(), Qt::Horizontal); + QCOMPARE(headerDataChangedSpy[1][1].value<int>(), 0); + QCOMPARE(headerDataChangedSpy[1][2].value<int>(), 4); + + for (int i = 0; i < proxy.columnCount(); ++i) { + QString expected = QString("Col%1").arg(i + 1); + QCOMPARE(proxy.headerData(i, Qt::Horizontal).toString(), expected); + } +} + void tst_QAbstractProxyModel::itemData_data() { QTest::addColumn<QModelIndex>("index"); @@ -186,7 +298,7 @@ void tst_QAbstractProxyModel::itemData() QFETCH(QModelIndex, index); QFETCH(int, count); SubQAbstractProxyModel model; - QCOMPARE(model.itemData(index).count(), count); + QCOMPARE(model.itemData(index).size(), count); } void tst_QAbstractProxyModel::mapFromSource_data() @@ -399,7 +511,7 @@ class SwappingProxy : public QAbstractProxyModel } } public: - virtual QModelIndex index(int row, int column, const QModelIndex &parentIdx) const + virtual QModelIndex index(int row, int column, const QModelIndex &parentIdx) const override { if (!sourceModel()) return QModelIndex(); @@ -412,28 +524,28 @@ public: return createIndex(row, column, parentIdx.internalPointer()); } - virtual QModelIndex parent(const QModelIndex &parentIdx) const + virtual QModelIndex parent(const QModelIndex &parentIdx) const override { // well, we're a 2D model Q_UNUSED(parentIdx); return QModelIndex(); } - virtual int rowCount(const QModelIndex &parentIdx) const + virtual int rowCount(const QModelIndex &parentIdx) const override { if (parentIdx.isValid() || !sourceModel()) return 0; return sourceModel()->rowCount(); } - virtual int columnCount(const QModelIndex &parentIdx) const + virtual int columnCount(const QModelIndex &parentIdx) const override { if (parentIdx.isValid() || !sourceModel()) return 0; return sourceModel()->rowCount(); } - virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const + virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override { if (!proxyIndex.isValid()) return QModelIndex(); @@ -443,7 +555,7 @@ public: return sourceModel()->index(swapRow(proxyIndex.row()), proxyIndex.column(), QModelIndex()); } - virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const + virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override { if (!sourceIndex.isValid()) return QModelIndex(); @@ -485,9 +597,9 @@ void tst_QAbstractProxyModel::testSwappingRowsProxy() class StandardItemModelWithCustomDragAndDrop : public QStandardItemModel { public: - QStringList mimeTypes() const { return QStringList() << QStringLiteral("foo/mimetype"); } - Qt::DropActions supportedDragActions() const { return Qt::CopyAction | Qt::LinkAction; } - Qt::DropActions supportedDropActions() const { return Qt::MoveAction; } + QStringList mimeTypes() const override { return QStringList() << QStringLiteral("foo/mimetype"); } + Qt::DropActions supportedDragActions() const override { return Qt::CopyAction | Qt::LinkAction; } + Qt::DropActions supportedDropActions() const override { return Qt::MoveAction; } }; void tst_QAbstractProxyModel::testDragAndDrop() @@ -500,6 +612,36 @@ void tst_QAbstractProxyModel::testDragAndDrop() QCOMPARE(proxy.supportedDropActions(), sourceModel.supportedDropActions()); } +void tst_QAbstractProxyModel::sourceModelBinding() +{ + SubQAbstractProxyModel proxy; + QStandardItemModel model1; + QStandardItemModel model2; + const char *lhs; + const char *rhs; + + auto printOnFailure = qScopeGuard([&] { + qDebug("Failed %s - %s test", lhs, rhs); + }); + lhs = "model"; + rhs = "model"; + QTestPrivate::testReadWritePropertyBasics<SubQAbstractProxyModel, QAbstractItemModel *>( + proxy, &model1, &model2, "sourceModel"); + + proxy.setSourceModel(&model2); + lhs = "model"; + rhs = "nullptr"; + QTestPrivate::testReadWritePropertyBasics<SubQAbstractProxyModel, QAbstractItemModel *>( + proxy, &model1, nullptr, "sourceModel"); + + proxy.setSourceModel(&model1); + lhs = "nullptr"; + rhs = "model"; + QTestPrivate::testReadWritePropertyBasics<SubQAbstractProxyModel, QAbstractItemModel *>( + proxy, nullptr, &model2, "sourceModel"); + + printOnFailure.dismiss(); +} QTEST_MAIN(tst_QAbstractProxyModel) #include "tst_qabstractproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/CMakeLists.txt new file mode 100644 index 0000000000..37a74470af --- /dev/null +++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qconcatenatetablesproxymodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qconcatenatetablesproxymodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qconcatenatetablesproxymodel + SOURCES + tst_qconcatenatetablesproxymodel.cpp + LIBRARIES + Qt::Gui +) diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro deleted file mode 100644 index ee4ea28b5b..0000000000 --- a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/qconcatenatetablesproxymodel.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qconcatenatetablesproxymodel -QT = core gui testlib - -SOURCES = tst_qconcatenatetablesproxymodel.cpp diff --git a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp index 40617c1f7d..989a57b30f 100644 --- a/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qconcatenatetablesproxymodel/tst_qconcatenatetablesproxymodel.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QSignalSpy> #include <QSortFilterProxyModel> @@ -82,9 +46,9 @@ static QString rowSpyToText(const QSignalSpy &spy) if (!spy.isValid()) return QStringLiteral("THE SIGNALSPY IS INVALID!"); QString str; - for (int i = 0; i < spy.count(); ++i) { + for (int i = 0; i < spy.size(); ++i) { str += spy.at(i).at(1).toString() + QLatin1Char(',') + spy.at(i).at(2).toString(); - if (i + 1 < spy.count()) + if (i + 1 < spy.size()) str += QLatin1Char(';'); } return str; @@ -116,7 +80,9 @@ private Q_SLOTS: void shouldPropagateDropBetweenItemsAtModelBoundary(); void shouldPropagateDropAfterLastRow_data(); void shouldPropagateDropAfterLastRow(); - + void qtbug91788(); + void qtbug91878(); + void createPersistentOnLayoutAboutToBeChanged(); private: QStandardItemModel mod; QStandardItemModel mod2; @@ -183,8 +149,8 @@ void tst_QConcatenateTablesProxyModel::shouldAggregateThenRemoveTwoEmptyModelsCo // Then the proxy should still be empty (and no signals emitted) QCOMPARE(pm.rowCount(), 0); QCOMPARE(pm.columnCount(), 0); - QCOMPARE(rowATBISpy.count(), 0); - QCOMPARE(rowInsertedSpy.count(), 0); + QCOMPARE(rowATBISpy.size(), 0); + QCOMPARE(rowInsertedSpy.size(), 0); // When removing the empty models pm.removeSourceModel(&i1); @@ -193,8 +159,8 @@ void tst_QConcatenateTablesProxyModel::shouldAggregateThenRemoveTwoEmptyModelsCo // Then the proxy should still be empty (and no signals emitted) QCOMPARE(pm.rowCount(), 0); QCOMPARE(pm.columnCount(), 0); - QCOMPARE(rowATBRSpy.count(), 0); - QCOMPARE(rowRemovedSpy.count(), 0); + QCOMPARE(rowATBRSpy.size(), 0); + QCOMPARE(rowRemovedSpy.size(), 0); } void tst_QConcatenateTablesProxyModel::shouldAggregateTwoEmptyModelsWhichThenGetFilled() @@ -239,14 +205,14 @@ void tst_QConcatenateTablesProxyModel::shouldHandleDataChanged() mod.item(0, 0)->setData("a", Qt::EditRole); // Then the change should be notified to the proxy - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); // Same test with the other model mod2.item(0, 2)->setData("f", Qt::EditRole); - QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); } @@ -264,14 +230,14 @@ void tst_QConcatenateTablesProxyModel::shouldHandleSetData() pm.setData(pm.index(0, 0), "a"); // Then the change should be notified to the proxy - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("aBC")); // Same test with the other model pm.setData(pm.index(1, 2), "f"); - QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEf")); } @@ -290,7 +256,7 @@ void tst_QConcatenateTablesProxyModel::shouldHandleSetItemData() std::make_pair<int, QVariant>(Qt::UserRole, 88) }); // Then the change should be notified to the proxy - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("XBC")); QCOMPARE(pm.index(0, 0).data(Qt::UserRole).toInt(), 88); @@ -299,7 +265,7 @@ void tst_QConcatenateTablesProxyModel::shouldHandleSetItemData() pm.setItemData(pm.index(1, 2), QMap<int, QVariant>{ std::make_pair<int, QVariant>(Qt::DisplayRole, QStringLiteral("Y")), std::make_pair<int, QVariant>(Qt::UserRole, 89) }); - QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.size(), 2); QCOMPARE(dataChangedSpy.at(1).at(0).toModelIndex(), pm.index(1, 2)); QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEY")); QCOMPARE(pm.index(1, 2).data(Qt::UserRole).toInt(), 89); @@ -336,10 +302,10 @@ void tst_QConcatenateTablesProxyModel::shouldHandleRowInsertionAndRemoval() mod2.removeRow(0); // Then the proxy should notify its users and show changes - QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 1); QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); - QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); QCOMPARE(pm.rowCount(), 2); @@ -352,10 +318,10 @@ void tst_QConcatenateTablesProxyModel::shouldHandleRowInsertionAndRemoval() mod2.removeRow(0); // Then the proxy should notify its users and show changes - QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 1); QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); - QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); QCOMPARE(pm.rowCount(), 1); @@ -391,10 +357,10 @@ void tst_QConcatenateTablesProxyModel::shouldAggregateAnotherModelThenRemoveMode pm.removeSourceModel(&mod3); // Then the proxy should notify its users about the row removed - QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 1); QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 2); QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 3); - QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 2); QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 3); QCOMPARE(pm.rowCount(), 2); @@ -405,10 +371,10 @@ void tst_QConcatenateTablesProxyModel::shouldAggregateAnotherModelThenRemoveMode rowATBRSpy.clear(); rowRemovedSpy.clear(); pm.removeSourceModel(&mod2); - QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 1); QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 1); - QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 1); QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 1); QCOMPARE(pm.rowCount(), 1); @@ -418,10 +384,10 @@ void tst_QConcatenateTablesProxyModel::shouldAggregateAnotherModelThenRemoveMode rowATBRSpy.clear(); rowRemovedSpy.clear(); pm.removeSourceModel(&mod); - QCOMPARE(rowATBRSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 1); QCOMPARE(rowATBRSpy.at(0).at(1).toInt(), 0); QCOMPARE(rowATBRSpy.at(0).at(2).toInt(), 0); - QCOMPARE(rowRemovedSpy.count(), 1); + QCOMPARE(rowRemovedSpy.size(), 1); QCOMPARE(rowRemovedSpy.at(0).at(1).toInt(), 0); QCOMPARE(rowRemovedSpy.at(0).at(2).toInt(), 0); QCOMPARE(pm.rowCount(), 0); @@ -453,6 +419,17 @@ void tst_QConcatenateTablesProxyModel::shouldUseSmallestColumnCount() const QModelIndex indexD = pm.mapFromSource(mod2.index(0, 0)); QVERIFY(indexD.isValid()); QCOMPARE(indexD, pm.index(1, 0)); + + // Test setData in an ignored column (QTBUG-91253) + QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + mod.setData(mod.index(0, 1), "b"); + QCOMPARE(dataChangedSpy.size(), 0); + + // Test dataChanged across all columns, some visible, some ignored + mod.dataChanged(mod.index(0, 0), mod.index(0, 2)); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(dataChangedSpy.at(0).at(0).toModelIndex(), pm.index(0, 0)); + QCOMPARE(dataChangedSpy.at(0).at(1).toModelIndex(), pm.index(0, 0)); } void tst_QConcatenateTablesProxyModel::shouldIncreaseColumnCountWhenRemovingFirstModel() @@ -540,8 +517,8 @@ void tst_QConcatenateTablesProxyModel::shouldPropagateLayoutChanged() QItemSelectionModel selection(&pm); selection.select(pm.index(1, 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); const QModelIndexList lst = selection.selectedIndexes(); - QCOMPARE(lst.count(), 3); - for (int col = 0; col < lst.count(); ++col) { + QCOMPARE(lst.size(), 3); + for (int col = 0; col < lst.size(); ++col) { QCOMPARE(lst.at(col).row(), 1); QCOMPARE(lst.at(col).column(), col); } @@ -556,13 +533,13 @@ void tst_QConcatenateTablesProxyModel::shouldPropagateLayoutChanged() QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("456")); QCOMPARE(extractRowTexts(&pm, 2), QStringLiteral("123")); - QCOMPARE(layoutATBCSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), 1); + QCOMPARE(layoutATBCSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); // And the selection should be updated accordingly (it became row 2) const QModelIndexList lstAfter = selection.selectedIndexes(); - QCOMPARE(lstAfter.count(), 3); - for (int col = 0; col < lstAfter.count(); ++col) { + QCOMPARE(lstAfter.size(), 3); + for (int col = 0; col < lstAfter.size(); ++col) { QCOMPARE(lstAfter.at(col).row(), 2); QCOMPARE(lstAfter.at(col).column(), col); } @@ -597,14 +574,14 @@ void tst_QConcatenateTablesProxyModel::shouldReactToModelReset() // Then the proxy should emit the reset signals, and show the new data QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("ABC")); QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("DEF")); - QCOMPARE(rowATBRSpy.count(), 0); - QCOMPARE(rowRemovedSpy.count(), 0); - QCOMPARE(rowATBISpy.count(), 0); - QCOMPARE(rowInsertedSpy.count(), 0); - QCOMPARE(colATBRSpy.count(), 0); - QCOMPARE(colRemovedSpy.count(), 0); - QCOMPARE(modelATBResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); + QCOMPARE(rowATBRSpy.size(), 0); + QCOMPARE(rowRemovedSpy.size(), 0); + QCOMPARE(rowATBISpy.size(), 0); + QCOMPARE(rowInsertedSpy.size(), 0); + QCOMPARE(colATBRSpy.size(), 0); + QCOMPARE(colRemovedSpy.size(), 0); + QCOMPARE(modelATBResetSpy.size(), 1); + QCOMPARE(modelResetSpy.size(), 1); } void tst_QConcatenateTablesProxyModel::shouldUpdateColumnsOnModelReset() @@ -639,14 +616,14 @@ void tst_QConcatenateTablesProxyModel::shouldUpdateColumnsOnModelReset() qsfpm.setSourceModel(&mod2Columns); // Then the proxy should reset, and show the new data - QCOMPARE(modelATBResetSpy.count(), 1); - QCOMPARE(modelResetSpy.count(), 1); - QCOMPARE(rowATBRSpy.count(), 0); - QCOMPARE(rowRemovedSpy.count(), 0); - QCOMPARE(rowATBISpy.count(), 0); - QCOMPARE(rowInsertedSpy.count(), 0); - QCOMPARE(colATBRSpy.count(), 0); - QCOMPARE(colRemovedSpy.count(), 0); + QCOMPARE(modelATBResetSpy.size(), 1); + QCOMPARE(modelResetSpy.size(), 1); + QCOMPARE(rowATBRSpy.size(), 0); + QCOMPARE(rowRemovedSpy.size(), 0); + QCOMPARE(rowATBISpy.size(), 0); + QCOMPARE(rowInsertedSpy.size(), 0); + QCOMPARE(colATBRSpy.size(), 0); + QCOMPARE(colRemovedSpy.size(), 0); QCOMPARE(pm.rowCount(), 2); QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("WX")); @@ -818,6 +795,73 @@ void tst_QConcatenateTablesProxyModel::shouldPropagateDropAfterLastRow() } +void tst_QConcatenateTablesProxyModel::qtbug91788() +{ + QConcatenateTablesProxyModel proxyConcat; + QStringList strList{QString("one"),QString("two")}; + QStringListModel strListModelA(strList); + QSortFilterProxyModel proxyFilter; + proxyFilter.setSourceModel(&proxyConcat); + + proxyConcat.addSourceModel(&strListModelA); + proxyConcat.removeSourceModel(&strListModelA); // This should not assert + QCOMPARE(proxyConcat.columnCount(), 0); +} + +void tst_QConcatenateTablesProxyModel::qtbug91878() +{ + QStandardItemModel m; + m.setRowCount(4); + m.setColumnCount(4); + + QConcatenateTablesProxyModel pm; + QSortFilterProxyModel proxyFilter; + proxyFilter.setSourceModel(&pm); + proxyFilter.setFilterFixedString("something"); + pm.addSourceModel(&m); // This should not assert + + QCOMPARE(pm.columnCount(), 4); + QCOMPARE(pm.rowCount(), 4); +} + +void tst_QConcatenateTablesProxyModel::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QStandardItemModel model1(3, 1); + QStandardItemModel model2(3, 1); + for (int row = 0; row < 3; ++row) { + model1.setData(model1.index(row, 0), row); + model2.setData(model2.index(row, 0), row + 5); + } + QConcatenateTablesProxyModel proxy; + new QAbstractItemModelTester(&proxy, &proxy); + proxy.addSourceModel(&model1); + proxy.addSourceModel(&model2); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(&proxy, &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &proxy](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(proxy.index(row, 0)); + }); + connect(&proxy, &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + QVERIFY(model1.setData(model1.index(1, 0), -1)); + model1.sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_GUILESS_MAIN(tst_QConcatenateTablesProxyModel) #include "tst_qconcatenatetablesproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qidentityproxymodel/CMakeLists.txt new file mode 100644 index 0000000000..87a2cdcba1 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qidentityproxymodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qidentityproxymodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qidentityproxymodel + SOURCES + ../../../other/qabstractitemmodelutils/dynamictreemodel.cpp ../../../other/qabstractitemmodelutils/dynamictreemodel.h + tst_qidentityproxymodel.cpp + INCLUDE_DIRECTORIES + ../../../other/qabstractitemmodelutils + LIBRARIES + Qt::Gui +) diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro b/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro deleted file mode 100644 index ddec0d2354..0000000000 --- a/tests/auto/corelib/itemmodels/qidentityproxymodel/qidentityproxymodel.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -TARGET = tst_qidentityproxymodel - -mtdir = ../../../other/qabstractitemmodelutils -INCLUDEPATH += $$PWD/$${mtdir} -QT += testlib -SOURCES += tst_qidentityproxymodel.cpp $${mtdir}/dynamictreemodel.cpp -HEADERS += $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp index 149d272594..99b74bc09a 100644 --- a/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qidentityproxymodel/tst_qidentityproxymodel.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Stephen Kelly <stephen.kelly@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QAbstractItemModelTester> #include <QCoreApplication> @@ -43,15 +18,25 @@ Q_LOGGING_CATEGORY(lcItemModels, "qt.corelib.tests.itemmodels") class DataChangedModel : public QAbstractListModel { public: - int rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 1; } - QVariant data(const QModelIndex&, int) const { return QVariant(); } - QModelIndex index(int row, int column, const QModelIndex &) const { return createIndex(row, column); } + int rowCount(const QModelIndex &parent) const override { return parent.isValid() ? 0 : 1; } + QVariant data(const QModelIndex&, int) const override { return QVariant(); } + QModelIndex index(int row, int column, const QModelIndex &) const override { return createIndex(row, column); } void changeData() { const QModelIndex idx = index(0, 0, QModelIndex()); - Q_EMIT dataChanged(idx, idx, QVector<int>() << 1); + Q_EMIT dataChanged(idx, idx, QList<int>() << 1); } + + // Workaround QObject::isSignalConnected() being a protected method + bool isConnected(const QMetaMethod &m) const { return isSignalConnected(m); } +}; + +class IdentityProxyModel : public QIdentityProxyModel +{ +public: + // The name has to be different than the method from the base class + void setHandleSLC(bool b) { setHandleSourceLayoutChanges(b); } }; class tst_QIdentityProxyModel : public QObject @@ -77,6 +62,9 @@ private slots: void itemData(); void persistIndexOnLayoutChange(); + void createPersistentOnLayoutAboutToBeChanged(); + + void testSetHandleLayoutChanges(); protected: void verifyIdentity(QAbstractItemModel *model, const QModelIndex &parent = QModelIndex()); @@ -94,7 +82,7 @@ tst_QIdentityProxyModel::tst_QIdentityProxyModel() void tst_QIdentityProxyModel::initTestCase() { - qRegisterMetaType<QVector<int> >(); + qRegisterMetaType<QList<int> >(); m_model = new QStandardItemModel(0, 1); m_proxy = new QIdentityProxyModel(); m_modelTest = new QAbstractItemModelTester(m_proxy, this); @@ -389,7 +377,7 @@ void tst_QIdentityProxyModel::dataChanged() model.changeData(); - QCOMPARE(modelSpy.first().at(2).value<QVector<int> >(), QVector<int>() << 1); + QCOMPARE(modelSpy.first().at(2).value<QList<int> >(), QList<int>() << 1); QCOMPARE(modelSpy.first().at(2), proxySpy.first().at(2)); verifyIdentity(&model); @@ -399,13 +387,22 @@ void tst_QIdentityProxyModel::dataChanged() class AppendStringProxy : public QIdentityProxyModel { public: - QVariant data(const QModelIndex &index, int role) const + QVariant data(const QModelIndex &index, int role) const override { const QVariant result = QIdentityProxyModel::data(index, role); if (role != Qt::DisplayRole) return result; - return result.toString() + "_appended"; + return result.toString() + QLatin1String("_appended"); } + QMap<int, QVariant> itemData(const QModelIndex &index) const override + { + QMap<int, QVariant> result = QIdentityProxyModel::itemData(index); + auto displayIter = result.find(Qt::DisplayRole); + if (displayIter != result.end()) + displayIter.value() = displayIter.value().toString() + QLatin1String("_appended"); + return result; + } + }; void tst_QIdentityProxyModel::itemData() @@ -494,5 +491,62 @@ void tst_QIdentityProxyModel::persistIndexOnLayoutChange() QVERIFY(persistentIndex.isValid()); } +void tst_QIdentityProxyModel::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QStandardItemModel model(3, 1); + for (int row = 0; row < 3; ++row) + model.setData(model.index(row, 0), row, Qt::UserRole); + model.setSortRole(Qt::UserRole); + QIdentityProxyModel proxy; + new QAbstractItemModelTester(&proxy, &proxy); + proxy.setSourceModel(&model); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(&proxy, &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &proxy](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(proxy.index(row, 0)); + }); + connect(&proxy, &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data(Qt::UserRole).toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data(Qt::UserRole).toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data(Qt::UserRole).toInt(), 2); + }); + model.setData(model.index(1, 0), -1, Qt::UserRole); + model.sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QIdentityProxyModel::testSetHandleLayoutChanges() +{ + const std::array layoutSignals = { + QMetaMethod::fromSignal(&QAbstractItemModel::layoutChanged), + QMetaMethod::fromSignal(&QAbstractItemModel::layoutAboutToBeChanged), + }; + + DataChangedModel model; + IdentityProxyModel proxy; + proxy.setSourceModel(&model); + for (const auto &m : layoutSignals) + QVERIFY(model.isConnected(m)); // Connected by default + + proxy.setSourceModel(nullptr); + + // Disable handling (connecting to layotu signals) of source model layout changes + proxy.setHandleSLC(false); + proxy.setSourceModel(&model); + for (const auto &m : layoutSignals) + QVERIFY(!model.isConnected(m)); +} + QTEST_MAIN(tst_QIdentityProxyModel) #include "tst_qidentityproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qitemmodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qitemmodel/CMakeLists.txt new file mode 100644 index 0000000000..57d9ba4913 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qitemmodel/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qitemmodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qitemmodel + SOURCES + tst_qitemmodel.cpp + LIBRARIES + Qt::Gui + Qt::Sql + Qt::Widgets +) diff --git a/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp b/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp index d61d45bc52..b2d507875d 100644 --- a/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp +++ b/tests/auto/corelib/itemmodels/qitemmodel/modelstotest.cpp @@ -1,33 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include <QtTest/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + + +#include <QTest> #include <QtCore/QCoreApplication> #include <QtSql/QtSql> #include <QtWidgets/QtWidgets> @@ -83,16 +58,13 @@ private: Add new tests, they can be the same model, but in a different state. The name of the model is passed to createModel - If readOnly is true the remove tests will be skipped. Example: QDirModel is disabled. - If createModel returns an empty model. Example: QDirModel does not + If readOnly is true the remove tests will be skipped. Example: QSqlQueryModel is disabled. + If createModel returns an empty model. */ ModelsToTest::ModelsToTest() { setupDatabase(); -#if QT_CONFIG(dirmodel) && QT_DEPRECATED_SINCE(5, 15) - tests.append(test("QDirModel", ReadOnly, HasData)); -#endif tests.append(test("QStringListModel", ReadWrite, HasData)); tests.append(test("QStringListModelEmpty", ReadWrite, Empty)); @@ -145,14 +117,14 @@ QAbstractItemModel *ModelsToTest::createModel(const QString &modelType) if (modelType == "QSortFilterProxyModelEmpty") { QSortFilterProxyModel *model = new QSortFilterProxyModel; - QStandardItemModel *standardItemModel = new QStandardItemModel; + QStandardItemModel *standardItemModel = new QStandardItemModel(model); model->setSourceModel(standardItemModel); return model; } if (modelType == "QSortFilterProxyModelRegExp") { QSortFilterProxyModel *model = new QSortFilterProxyModel; - QStandardItemModel *standardItemModel = new QStandardItemModel; + QStandardItemModel *standardItemModel = new QStandardItemModel(model); model->setSourceModel(standardItemModel); populateTestArea(model); model->setFilterRegularExpression(QRegularExpression("(^$|I.*)")); @@ -161,23 +133,12 @@ QAbstractItemModel *ModelsToTest::createModel(const QString &modelType) if (modelType == "QSortFilterProxyModel") { QSortFilterProxyModel *model = new QSortFilterProxyModel; - QStandardItemModel *standardItemModel = new QStandardItemModel; + QStandardItemModel *standardItemModel = new QStandardItemModel(model); model->setSourceModel(standardItemModel); populateTestArea(model); return model; } -#if QT_CONFIG(dirmodel) && QT_DEPRECATED_SINCE(5, 15) -QT_WARNING_PUSH -QT_WARNING_DISABLE_DEPRECATED - if (modelType == "QDirModel") { - QDirModel *model = new QDirModel(); - model->setReadOnly(false); - return model; - } -QT_WARNING_POP -#endif - if (modelType == "QSqlQueryModel") { QSqlQueryModel *model = new QSqlQueryModel(); populateTestArea(model); @@ -294,25 +255,6 @@ QModelIndex ModelsToTest::populateTestArea(QAbstractItemModel *model) return returnIndex; } -#if QT_CONFIG(dirmodel) && QT_DEPRECATED_SINCE(5, 15) - if (QDirModel *dirModel = qobject_cast<QDirModel *>(model)) { - m_dirModelTempDir.reset(new QTemporaryDir); - if (!m_dirModelTempDir->isValid()) - qFatal("Cannot create temporary directory \"%s\": %s", - qPrintable(QDir::toNativeSeparators(m_dirModelTempDir->path())), - qPrintable(m_dirModelTempDir->errorString())); - - QDir tempDir(m_dirModelTempDir->path()); - for (int i = 0; i < 26; ++i) { - const QString subdir = QLatin1String("foo_") + QString::number(i); - if (!tempDir.mkdir(subdir)) - qFatal("Cannot create directory %s", - qPrintable(QDir::toNativeSeparators(tempDir.path() + QLatin1Char('/') +subdir))); - } - return dirModel->index(tempDir.path()); - } -#endif // QT_CONFIG(dirmodel) && QT_DEPRECATED_SINCE(5, 15) - if (QSqlQueryModel *queryModel = qobject_cast<QSqlQueryModel *>(model)) { QSqlQuery q; q.exec("CREATE TABLE test(id int primary key, name varchar(30))"); @@ -370,10 +312,6 @@ void ModelsToTest::cleanupTestArea(QAbstractItemModel *model) { if (qobject_cast<QSqlQueryModel *>(model)) QSqlQuery q("DROP TABLE test"); -#if QT_CONFIG(dirmodel) && QT_DEPRECATED_SINCE(5, 15) - if (qobject_cast<QDirModel *>(model)) - m_dirModelTempDir.reset(); -#endif } void ModelsToTest::setupDatabase() diff --git a/tests/auto/corelib/itemmodels/qitemmodel/qitemmodel.pro b/tests/auto/corelib/itemmodels/qitemmodel/qitemmodel.pro deleted file mode 100644 index ff21d6afa5..0000000000 --- a/tests/auto/corelib/itemmodels/qitemmodel/qitemmodel.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qitemmodel -QT += widgets sql testlib -SOURCES = tst_qitemmodel.cpp diff --git a/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp b/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp index b9deb7b6a9..b1f91fe9a3 100644 --- a/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp +++ b/tests/auto/corelib/itemmodels/qitemmodel/tst_qitemmodel.cpp @@ -1,32 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QSignalSpy> #include <QtCore/QCoreApplication> #include <qdebug.h> #include "modelstotest.cpp" @@ -611,7 +587,7 @@ void tst_QItemModel::setData() QVERIFY(currentModel); QSignalSpy spy(currentModel, &QAbstractItemModel::dataChanged); QVERIFY(spy.isValid()); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QFETCH(bool, isEmpty); if (isEmpty) @@ -635,7 +611,7 @@ void tst_QItemModel::setData() // Changing the text shouldn't change the layout, parent, pointer etc. QModelIndex changedIndex = currentModel->index(0, 0, topIndex); QCOMPARE(changedIndex, index); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QItemModel::setHeaderData_data() @@ -685,7 +661,7 @@ void tst_QItemModel::setHeaderData() ++signalCount; } } - QCOMPARE(spy.count(), signalCount); + QCOMPARE(spy.size(), signalCount); } void tst_QItemModel::sort_data() @@ -716,7 +692,7 @@ void tst_QItemModel::sort() for (int i=-1; i < 10; ++i){ currentModel->sort(i); if (index != currentModel->index(0, 0, topIndex)){ - QVERIFY(spy.count() > 0); + QVERIFY(spy.size() > 0); index = currentModel->index(0, 0, topIndex); spy.clear(); } @@ -882,7 +858,7 @@ void tst_QItemModel::remove() if (shouldSucceed && dyingIndex.isValid()) QCOMPARE(dyingIndex.row(), start + 1); - if (rowsAboutToBeRemovedSpy.count() > 0){ + if (rowsAboutToBeRemovedSpy.size() > 0){ QList<QVariant> arguments = rowsAboutToBeRemovedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -892,7 +868,7 @@ void tst_QItemModel::remove() QVERIFY(parentOfRemoved == parent); } - if (rowsRemovedSpy.count() > 0){ + if (rowsRemovedSpy.size() > 0){ QList<QVariant> arguments = rowsRemovedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -903,26 +879,26 @@ void tst_QItemModel::remove() } // Only the row signals should have been emitted - if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >=1 ){ - QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0); - QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0); - QCOMPARE(columnsRemovedSpy.count(), 0); - QCOMPARE(rowsRemovedSpy.count(), 0); + if (modelResetSpy.size() >= 1 || modelLayoutChangedSpy.size() >=1 ){ + QCOMPARE(columnsAboutToBeRemovedSpy.size(), 0); + QCOMPARE(rowsAboutToBeRemovedSpy.size(), 0); + QCOMPARE(columnsRemovedSpy.size(), 0); + QCOMPARE(rowsRemovedSpy.size(), 0); } else { - QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0); - QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals); - QCOMPARE(columnsRemovedSpy.count(), 0); - QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals); + QCOMPARE(columnsAboutToBeRemovedSpy.size(), 0); + QCOMPARE(rowsAboutToBeRemovedSpy.size(), numberOfRowsAboutToBeRemovedSignals); + QCOMPARE(columnsRemovedSpy.size(), 0); + QCOMPARE(rowsRemovedSpy.size(), numberOfRowsRemovedSignals); } // The row count should only change *after* rowsAboutToBeRemoved has been emitted if (shouldSucceed) { - if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){ + if (modelResetSpy.size() == 0 && modelLayoutChangedSpy.size() == 0){ QCOMPARE(afterAboutToRemoveRowCount, beforeRemoveRowCount); QCOMPARE(afterRemoveRowCount, beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1)); } - if (modelResetSpy.count() == 0 ) + if (modelResetSpy.size() == 0 ) QCOMPARE(currentModel->rowCount(parentOfRemoved), beforeRemoveRowCount-count-(numberOfRowsRemovedSignals-1)); } else { @@ -937,7 +913,7 @@ void tst_QItemModel::remove() disconnect(currentModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slot_rowsRemoved(QModelIndex))); modelResetSpy.clear(); - QCOMPARE(modelResetSpy.count(), 0); + QCOMPARE(modelResetSpy.size(), 0); // // Test remove column @@ -952,26 +928,26 @@ void tst_QItemModel::remove() if (currentModel->removeColumns(start, count, parentOfRemoved)) { currentModel->submit(); // Didn't reset the rows, so they should still be at the same value - if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1){ - QCOMPARE(columnsAboutToBeRemovedSpy.count(), 0); + if (modelResetSpy.size() >= 1 || modelLayoutChangedSpy.size() >= 1){ + QCOMPARE(columnsAboutToBeRemovedSpy.size(), 0); //QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals); - QCOMPARE(columnsRemovedSpy.count(), 0); + QCOMPARE(columnsRemovedSpy.size(), 0); //QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals); } else { - QCOMPARE(columnsAboutToBeRemovedSpy.count(), numberOfColumnsAboutToBeRemovedSignals); - QCOMPARE(rowsAboutToBeRemovedSpy.count(), numberOfRowsAboutToBeRemovedSignals); - QCOMPARE(columnsRemovedSpy.count(), numberOfColumnsRemovedSignals); - QCOMPARE(rowsRemovedSpy.count(), numberOfRowsRemovedSignals); + QCOMPARE(columnsAboutToBeRemovedSpy.size(), numberOfColumnsAboutToBeRemovedSignals); + QCOMPARE(rowsAboutToBeRemovedSpy.size(), numberOfRowsAboutToBeRemovedSignals); + QCOMPARE(columnsRemovedSpy.size(), numberOfColumnsRemovedSignals); + QCOMPARE(rowsRemovedSpy.size(), numberOfRowsRemovedSignals); } // The column count should only change *after* rowsAboutToBeRemoved has been emitted if (shouldSucceed) { - if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0){ + if (modelResetSpy.size() == 0 && modelLayoutChangedSpy.size() == 0){ QCOMPARE(afterAboutToRemoveColumnCount, beforeRemoveColumnCount); QCOMPARE(afterRemoveColumnCount, beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1)); } - if (modelResetSpy.count() == 0) + if (modelResetSpy.size() == 0) QCOMPARE(currentModel->columnCount(parentOfRemoved), beforeRemoveColumnCount-count-(numberOfColumnsRemovedSignals-1)); } else @@ -982,7 +958,7 @@ void tst_QItemModel::remove() disconnect(currentModel, SIGNAL(columnsRemoved(QModelIndex,int,int)), this, SLOT(slot_columnsRemoved(QModelIndex))); - if (columnsAboutToBeRemovedSpy.count() > 0){ + if (columnsAboutToBeRemovedSpy.size() > 0){ QList<QVariant> arguments = columnsAboutToBeRemovedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -992,7 +968,7 @@ void tst_QItemModel::remove() QVERIFY(parentOfRemoved == parent); } - if (columnsRemovedSpy.count() > 0){ + if (columnsRemovedSpy.size() > 0){ QList<QVariant> arguments = columnsRemovedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -1221,7 +1197,7 @@ void tst_QItemModel::insert() QCOMPARE(currentModel->insertRows(start, count, parentOfInserted), shouldSucceed); currentModel->submit(); - if (rowsAboutToBeInsertedSpy.count() > 0){ + if (rowsAboutToBeInsertedSpy.size() > 0){ QList<QVariant> arguments = rowsAboutToBeInsertedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -1231,7 +1207,7 @@ void tst_QItemModel::insert() QVERIFY(parentOfInserted == parent); } - if (rowsInsertedSpy.count() > 0){ + if (rowsInsertedSpy.size() > 0){ QList<QVariant> arguments = rowsInsertedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -1242,25 +1218,25 @@ void tst_QItemModel::insert() } // Only the row signals should have been emitted - if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) { - QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0); - QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0); - QCOMPARE(columnsInsertedSpy.count(), 0); - QCOMPARE(rowsInsertedSpy.count(), 0); + if (modelResetSpy.size() >= 1 || modelLayoutChangedSpy.size() >= 1) { + QCOMPARE(columnsAboutToBeInsertedSpy.size(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.size(), 0); + QCOMPARE(columnsInsertedSpy.size(), 0); + QCOMPARE(rowsInsertedSpy.size(), 0); } else { - QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0); - QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals); - QCOMPARE(columnsInsertedSpy.count(), 0); - QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals); + QCOMPARE(columnsAboutToBeInsertedSpy.size(), 0); + QCOMPARE(rowsAboutToBeInsertedSpy.size(), numberOfRowsAboutToBeInsertedSignals); + QCOMPARE(columnsInsertedSpy.size(), 0); + QCOMPARE(rowsInsertedSpy.size(), numberOfRowsInsertedSignals); } // The row count should only change *after* rowsAboutToBeInserted has been emitted if (shouldSucceed) { - if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) { + if (modelResetSpy.size() == 0 && modelLayoutChangedSpy.size() == 0) { QCOMPARE(afterAboutToInsertRowCount, beforeInsertRowCount); QCOMPARE(afterInsertRowCount, beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1)); } - if (modelResetSpy.count() == 0) + if (modelResetSpy.size() == 0) QCOMPARE(currentModel->rowCount(parentOfInserted), beforeInsertRowCount+count+(numberOfRowsInsertedSignals-1)); } else { @@ -1288,27 +1264,27 @@ void tst_QItemModel::insert() // Some models don't let you insert the column, only row if (currentModel->insertColumns(start, count, parentOfInserted)) { currentModel->submit(); - if (modelResetSpy.count() >= 1 || modelLayoutChangedSpy.count() >= 1) { + if (modelResetSpy.size() >= 1 || modelLayoutChangedSpy.size() >= 1) { // Didn't reset the rows, so they should still be at the same value - QCOMPARE(columnsAboutToBeInsertedSpy.count(), 0); + QCOMPARE(columnsAboutToBeInsertedSpy.size(), 0); //QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals); - QCOMPARE(columnsInsertedSpy.count(), 0); + QCOMPARE(columnsInsertedSpy.size(), 0); //QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals); } else { // Didn't reset the rows, so they should still be at the same value - QCOMPARE(columnsAboutToBeInsertedSpy.count(), numberOfColumnsAboutToBeInsertedSignals); - QCOMPARE(rowsAboutToBeInsertedSpy.count(), numberOfRowsAboutToBeInsertedSignals); - QCOMPARE(columnsInsertedSpy.count(), numberOfColumnsInsertedSignals); - QCOMPARE(rowsInsertedSpy.count(), numberOfRowsInsertedSignals); + QCOMPARE(columnsAboutToBeInsertedSpy.size(), numberOfColumnsAboutToBeInsertedSignals); + QCOMPARE(rowsAboutToBeInsertedSpy.size(), numberOfRowsAboutToBeInsertedSignals); + QCOMPARE(columnsInsertedSpy.size(), numberOfColumnsInsertedSignals); + QCOMPARE(rowsInsertedSpy.size(), numberOfRowsInsertedSignals); } // The column count should only change *after* rowsAboutToBeInserted has been emitted if (shouldSucceed) { - if (modelResetSpy.count() == 0 && modelLayoutChangedSpy.count() == 0) { + if (modelResetSpy.size() == 0 && modelLayoutChangedSpy.size() == 0) { QCOMPARE(afterAboutToInsertColumnCount, beforeInsertColumnCount); QCOMPARE(afterInsertColumnCount, beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1)); } - if (modelResetSpy.count() == 0) + if (modelResetSpy.size() == 0) QCOMPARE(currentModel->columnCount(parentOfInserted), beforeInsertColumnCount+count+(numberOfColumnsInsertedSignals-1)); } else @@ -1319,7 +1295,7 @@ void tst_QItemModel::insert() disconnect(currentModel, SIGNAL(columnsInserted(QModelIndex,int,int)), this, SLOT(slot_columnsInserted(QModelIndex))); - if (columnsAboutToBeInsertedSpy.count() > 0){ + if (columnsAboutToBeInsertedSpy.size() > 0){ QList<QVariant> arguments = columnsAboutToBeInsertedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); @@ -1329,7 +1305,7 @@ void tst_QItemModel::insert() QVERIFY(parentOfInserted == parent); } - if (columnsInsertedSpy.count() > 0){ + if (columnsInsertedSpy.size() > 0){ QList<QVariant> arguments = columnsInsertedSpy.at(0); QModelIndex parent = (qvariant_cast<QModelIndex>(arguments.at(0))); int first = arguments.at(1).toInt(); diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qitemselectionmodel/CMakeLists.txt new file mode 100644 index 0000000000..88ec493305 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qitemselectionmodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemselectionmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qitemselectionmodel + SOURCES + tst_qitemselectionmodel.cpp + LIBRARIES + Qt::Gui + Qt::TestPrivate +) diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro b/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro deleted file mode 100644 index 4dcddc8feb..0000000000 --- a/tests/auto/corelib/itemmodels/qitemselectionmodel/qitemselectionmodel.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qitemselectionmodel -QT += testlib -SOURCES += tst_qitemselectionmodel.cpp diff --git a/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp index 6b59a6f1af..08233a1f7b 100644 --- a/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp +++ b/tests/auto/corelib/itemmodels/qitemselectionmodel/tst_qitemselectionmodel.cpp @@ -1,32 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> +#include <QtTest/private/qpropertytesthelper_p.h> +#include <QSignalSpy> #include <QtGui/QtGui> @@ -47,6 +25,7 @@ public slots: void cleanupTestCase(); void init(); private slots: + void compareCompiles(); void clear_data(); void clear(); void clearAndSelect(); @@ -78,14 +57,16 @@ private slots: void merge(); void isRowSelected(); void childrenDeselectionSignal(); +#if QT_CONFIG(proxymodel) void layoutChangedWithAllSelected1(); void layoutChangedWithAllSelected2(); +#endif void layoutChangedTreeSelection(); void deselectRemovedMiddleRange(); - void rangeOperatorLessThan_data(); - void rangeOperatorLessThan(); void setModel(); + void bindableModel(); + void testDifferentModels(); void testValidRangesInSelectionsAfterReset(); @@ -96,16 +77,26 @@ private slots: void QTBUG48402(); void QTBUG58851_data(); +#if QT_CONFIG(proxymodel) void QTBUG58851(); +#endif void QTBUG18001_data(); void QTBUG18001(); + void QTBUG93305(); + + void testSignalsDisconnection(); + void destroyModel(); + private: + static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); QAbstractItemModel *model; QItemSelectionModel *selection; }; +QT_BEGIN_NAMESPACE + QDataStream &operator<<(QDataStream &, const QModelIndex &); QDataStream &operator>>(QDataStream &, QModelIndex &); QDataStream &operator<<(QDataStream &, const QModelIndexList &); @@ -125,17 +116,17 @@ public: return helper.QAbstractItemModel::createIndex(row, column, data); } - QModelIndex index(int, int, const QModelIndex&) const + QModelIndex index(int, int, const QModelIndex&) const override { return QModelIndex(); } - QModelIndex parent(const QModelIndex&) const + QModelIndex parent(const QModelIndex&) const override { return QModelIndex(); } - int rowCount(const QModelIndex & = QModelIndex()) const + int rowCount(const QModelIndex & = QModelIndex()) const override { return 0; } - int columnCount(const QModelIndex & = QModelIndex()) const + int columnCount(const QModelIndex & = QModelIndex()) const override { return 0; } - QVariant data(const QModelIndex &, int = Qt::DisplayRole) const + QVariant data(const QModelIndex &, int = Qt::DisplayRole) const override { return QVariant(); } - bool hasChildren(const QModelIndex &) const + bool hasChildren(const QModelIndex &) const override { return false; } }; @@ -160,8 +151,8 @@ QDataStream &operator>>(QDataStream &s, QModelIndex &output) QDataStream &operator<<(QDataStream &s, const QModelIndexList &input) { - s << input.count(); - for (int i=0; i<input.count(); ++i) + s << input.size(); + for (int i=0; i<input.size(); ++i) s << input.at(i); return s; } @@ -178,6 +169,8 @@ QDataStream &operator>>(QDataStream &s, QModelIndexList &output) return s; } +QT_END_NAMESPACE + tst_QItemSelectionModel::tst_QItemSelectionModel() : model(0), selection(0) { @@ -226,6 +219,11 @@ void tst_QItemSelectionModel::init() model->insertRow(0, QModelIndex()); } +void tst_QItemSelectionModel::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QItemSelectionRange>(); +} + void tst_QItemSelectionModel::clear_data() { QTest::addColumn<QModelIndexList>("indexList"); @@ -295,7 +293,7 @@ void tst_QItemSelectionModel::clear() QFETCH(IntList, commandList); // do selections - for (int i=0; i<indexList.count(); ++i) { + for (int i=0; i<indexList.size(); ++i) { selection->select(indexList.at(i), (QItemSelectionModel::SelectionFlags)commandList.at(i)); } // test that we have selected items @@ -309,7 +307,7 @@ void tst_QItemSelectionModel::clearAndSelect() { // populate selectionmodel selection->select(model->index(1, 1, QModelIndex()), QItemSelectionModel::Select); - QCOMPARE(selection->selectedIndexes().count(), 1); + QCOMPARE(selection->selectedIndexes().size(), 1); QVERIFY(selection->hasSelection()); // ClearAndSelect with empty selection @@ -327,26 +325,26 @@ void tst_QItemSelectionModel::toggleSelection() //and hasSelection returns the correct value selection->clearSelection(); - QCOMPARE(selection->selectedIndexes().count(), 0); + QCOMPARE(selection->selectedIndexes().size(), 0); QVERIFY(selection->hasSelection()==false); QModelIndex index=model->index(1, 1, QModelIndex()); // populate selectionmodel selection->select(index, QItemSelectionModel::Toggle); - QCOMPARE(selection->selectedIndexes().count(), 1); + QCOMPARE(selection->selectedIndexes().size(), 1); QVERIFY(selection->hasSelection()==true); selection->select(index, QItemSelectionModel::Toggle); - QCOMPARE(selection->selectedIndexes().count(), 0); + QCOMPARE(selection->selectedIndexes().size(), 0); QVERIFY(selection->hasSelection()==false); // populate selectionmodel with rows selection->select(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows); - QCOMPARE(selection->selectedIndexes().count(), model->columnCount()); + QCOMPARE(selection->selectedIndexes().size(), model->columnCount()); QVERIFY(selection->hasSelection()==true); selection->select(index, QItemSelectionModel::Toggle | QItemSelectionModel::Rows); - QCOMPARE(selection->selectedIndexes().count(), 0); + QCOMPARE(selection->selectedIndexes().size(), 0); QVERIFY(selection->hasSelection()==false); } @@ -1238,7 +1236,7 @@ void tst_QItemSelectionModel::select() int lastCommand = 0; // do selections - for (int i = 0; i<commandList.count(); ++i) { + for (int i = 0; i<commandList.size(); ++i) { if (useRanges) { selection->select(QItemSelection(indexList.at(2*i), indexList.at(2*i+1)), (QItemSelectionModel::SelectionFlags)commandList.at(i)); @@ -1255,13 +1253,13 @@ void tst_QItemSelectionModel::select() QVERIFY(selection->hasSelection()!=selectedList.isEmpty()); // test that the number of indices are as expected - QVERIFY2(selectedList.count() == expectedList.count(), + QVERIFY2(selectedList.size() == expectedList.size(), QString("expected indices: %1 actual indices: %2") - .arg(expectedList.count()) - .arg(selectedList.count()).toLatin1()); + .arg(expectedList.size()) + .arg(selectedList.size()).toLatin1()); // test existence of each index - for (int i=0; i<expectedList.count(); ++i) { + for (int i=0; i<expectedList.size(); ++i) { QVERIFY2(selectedList.contains(expectedList.at(i)), QString("expected index(%1, %2) not found in selectedIndexes()") .arg(expectedList.at(i).row()) @@ -1269,7 +1267,7 @@ void tst_QItemSelectionModel::select() } // test that isSelected agrees - for (int i=0; i<indexList.count(); ++i) { + for (int i=0; i<indexList.size(); ++i) { QModelIndex idx = indexList.at(i); QVERIFY2(selection->isSelected(idx) == selectedList.contains(idx), QString("isSelected(index: %1, %2) does not match selectedIndexes()") @@ -1280,7 +1278,7 @@ void tst_QItemSelectionModel::select() //for now we assume Rows/Columns flag is the same for all commands, therefore we just check lastCommand // test that isRowSelected agrees if (lastCommand & QItemSelectionModel::Rows) { - for (int i=0; i<selectedList.count(); ++i) + for (int i=0; i<selectedList.size(); ++i) QVERIFY2(selection->isRowSelected(selectedList.at(i).row(), model->parent(selectedList.at(i))), QString("isRowSelected(row: %1) does not match selectedIndexes()") @@ -1289,7 +1287,7 @@ void tst_QItemSelectionModel::select() // test that isColumnSelected agrees if (lastCommand & QItemSelectionModel::Columns) { - for (int i=0; i<selectedList.count(); ++i) + for (int i=0; i<selectedList.size(); ++i) QVERIFY2(selection->isColumnSelected(selectedList.at(i).column(), model->parent(selectedList.at(i))), QString("isColumnSelected(column: %1) does not match selectedIndexes()") @@ -1473,8 +1471,8 @@ void tst_QItemSelectionModel::persistentselections() QCOMPARE(model->columnCount(QModelIndex()), 5); // do selections - for (int i=0; i<commandList.count(); ++i) { - if (indexList.count() == commandList.count()) { + for (int i=0; i<commandList.size(); ++i) { + if (indexList.size() == commandList.size()) { QModelIndex index = model->index(indexList.at(i).first, indexList.at(i).second, QModelIndex()); @@ -1495,18 +1493,18 @@ void tst_QItemSelectionModel::persistentselections() QVERIFY(selection->hasSelection()); // insert/delete row and/or columns - if (insertRows.count() > 1) + if (insertRows.size() > 1) model->insertRows(insertRows.at(0), insertRows.at(1), QModelIndex()); - if (insertColumns.count() > 1) + if (insertColumns.size() > 1) model->insertColumns(insertColumns.at(0), insertColumns.at(1), QModelIndex()); - if (deleteRows.count() > 1) + if (deleteRows.size() > 1) model->removeRows(deleteRows.at(0), deleteRows.at(1), QModelIndex()); - if (deleteColumns.count() > 1) + if (deleteColumns.size() > 1) model->removeColumns(deleteColumns.at(0), deleteColumns.at(1), QModelIndex()); // check that the selected items are the correct number and indexes QModelIndexList selectedList = selection->selectedIndexes(); - QCOMPARE(selectedList.count(), expectedList.count()); + QCOMPARE(selectedList.size(), expectedList.size()); for (const auto &pair : expectedList) { QModelIndex index = model->index(pair.first, pair.second, QModelIndex()); QVERIFY(selectedList.contains(index)); @@ -1532,7 +1530,7 @@ void tst_QItemSelectionModel::resetModel() selectionModel->select(QItemSelection(model.index(0, 0), model.index(5, 5)), QItemSelectionModel::Select); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); model.reset(); @@ -1541,8 +1539,8 @@ void tst_QItemSelectionModel::resetModel() selectionModel->select(QItemSelection(model.index(0, 0), model.index(5, 5)), QItemSelectionModel::Select); - QCOMPARE(spy.count(), 2); - QCOMPARE(spy.at(1).count(), 2); + QCOMPARE(spy.size(), 2); + QCOMPARE(spy.at(1).size(), 2); // make sure we don't get an "old selection" QCOMPARE(spy.at(1).at(1).userType(), qMetaTypeId<QItemSelection>()); QVERIFY(qvariant_cast<QItemSelection>(spy.at(1).at(1)).isEmpty()); @@ -1597,14 +1595,14 @@ void tst_QItemSelectionModel::removeRows() QModelIndex br = model.index(selectBottom, selectRight); selections.select(QItemSelection(tl, br), QItemSelectionModel::ClearAndSelect); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(selections.isSelected(tl)); QVERIFY(selections.isSelected(br)); QVERIFY(selections.hasSelection()); model.removeRows(removeTop, removeBottom - removeTop + 1); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); tl = model.index(expectedTop, expectedLeft); br = model.index(expectedBottom, expectedRight); QVERIFY(selections.isSelected(tl)); @@ -1660,14 +1658,14 @@ void tst_QItemSelectionModel::removeColumns() QModelIndex br = model.index(selectBottom, selectRight); selections.select(QItemSelection(tl, br), QItemSelectionModel::ClearAndSelect); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QVERIFY(selections.isSelected(tl)); QVERIFY(selections.isSelected(br)); QVERIFY(selections.hasSelection()); model.removeColumns(removeLeft, removeRight - removeLeft + 1); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); tl = model.index(expectedTop, expectedLeft); br = model.index(expectedBottom, expectedRight); QVERIFY(selections.isSelected(tl)); @@ -1738,7 +1736,7 @@ void tst_QItemSelectionModel::modelLayoutChanged() QFETCH(int, sortColumn); QFETCH(IntPairPairList, expectedSelectedRanges); - MyStandardItemModel model(items.at(0).count(), items.count()); + MyStandardItemModel model(items.at(0).size(), items.size()); // initialize model data for (int i = 0; i < model.rowCount(); ++i) { for (int j = 0; j < model.columnCount(); ++j) { @@ -1763,10 +1761,10 @@ void tst_QItemSelectionModel::modelLayoutChanged() // verify that selection is as expected QItemSelection selection = selectionModel.selection(); - QCOMPARE(selection.count(), expectedSelectedRanges.count()); + QCOMPARE(selection.size(), expectedSelectedRanges.size()); QCOMPARE(selectionModel.hasSelection(), !expectedSelectedRanges.isEmpty()); - for (int i = 0; i < expectedSelectedRanges.count(); ++i) { + for (int i = 0; i < expectedSelectedRanges.size(); ++i) { IntPairPair expectedRange = expectedSelectedRanges.at(i); IntPair expectedTl = expectedRange.first; IntPair expectedBr = expectedRange.second; @@ -1820,21 +1818,21 @@ void tst_QItemSelectionModel::selectedRows() MyStandardItemModel model(rowCount, columnCount); QItemSelectionModel selectionModel(&model); - for (int i = 0; i < selectRows.count(); ++i) + for (int i = 0; i < selectRows.size(); ++i) selectionModel.select(model.index(selectRows.at(i), 0), QItemSelectionModel::Select |QItemSelectionModel::Rows); - for (int j = 0; j < selectRows.count(); ++j) + for (int j = 0; j < selectRows.size(); ++j) QVERIFY(selectionModel.isRowSelected(expectedRows.at(j), QModelIndex())); - for (int k = 0; k < selectRows.count(); ++k) + for (int k = 0; k < selectRows.size(); ++k) QVERIFY(!selectionModel.isRowSelected(unexpectedRows.at(k), QModelIndex())); QModelIndexList selectedRowIndexes = selectionModel.selectedRows(column); - QCOMPARE(selectedRowIndexes.count(), expectedRows.count()); + QCOMPARE(selectedRowIndexes.size(), expectedRows.size()); std::sort(selectedRowIndexes.begin(), selectedRowIndexes.end()); - for (int l = 0; l < selectedRowIndexes.count(); ++l) { + for (int l = 0; l < selectedRowIndexes.size(); ++l) { QCOMPARE(selectedRowIndexes.at(l).row(), expectedRows.at(l)); QCOMPARE(selectedRowIndexes.at(l).column(), column); } @@ -1880,21 +1878,21 @@ void tst_QItemSelectionModel::selectedColumns() MyStandardItemModel model(rowCount, columnCount); QItemSelectionModel selectionModel(&model); - for (int i = 0; i < selectColumns.count(); ++i) + for (int i = 0; i < selectColumns.size(); ++i) selectionModel.select(model.index(0, selectColumns.at(i)), QItemSelectionModel::Select |QItemSelectionModel::Columns); - for (int j = 0; j < selectColumns.count(); ++j) + for (int j = 0; j < selectColumns.size(); ++j) QVERIFY(selectionModel.isColumnSelected(expectedColumns.at(j), QModelIndex())); - for (int k = 0; k < selectColumns.count(); ++k) + for (int k = 0; k < selectColumns.size(); ++k) QVERIFY(!selectionModel.isColumnSelected(unexpectedColumns.at(k), QModelIndex())); QModelIndexList selectedColumnIndexes = selectionModel.selectedColumns(row); - QCOMPARE(selectedColumnIndexes.count(), expectedColumns.count()); + QCOMPARE(selectedColumnIndexes.size(), expectedColumns.size()); std::sort(selectedColumnIndexes.begin(), selectedColumnIndexes.end()); - for (int l = 0; l < selectedColumnIndexes.count(); ++l) { + for (int l = 0; l < selectedColumnIndexes.size(); ++l) { QCOMPARE(selectedColumnIndexes.at(l).column(), expectedColumns.at(l)); QCOMPARE(selectedColumnIndexes.at(l).row(), row); } @@ -1925,18 +1923,18 @@ void tst_QItemSelectionModel::setCurrentIndex() treemodel->index(0, 0, treemodel->index(1, 0)), QItemSelectionModel::SelectCurrent); - QCOMPARE(currentSpy.count(), 1); - QCOMPARE(rowSpy.count(), 1); - QCOMPARE(columnSpy.count(), 1); + QCOMPARE(currentSpy.size(), 1); + QCOMPARE(rowSpy.size(), 1); + QCOMPARE(columnSpy.size(), 1); // Select another row in the same parent selectionModel.setCurrentIndex( treemodel->index(1, 0, treemodel->index(1, 0)), QItemSelectionModel::SelectCurrent); - QCOMPARE(currentSpy.count(), 2); - QCOMPARE(rowSpy.count(), 2); - QCOMPARE(columnSpy.count(), 1); + QCOMPARE(currentSpy.size(), 2); + QCOMPARE(rowSpy.size(), 2); + QCOMPARE(columnSpy.size(), 1); } void tst_QItemSelectionModel::splitOnInsert() @@ -2060,12 +2058,14 @@ void tst_QItemSelectionModel::unselectable() } QItemSelectionModel selectionModel(&model); selectionModel.select(QItemSelection(model.index(0, 0), model.index(9, 0)), QItemSelectionModel::Select); - QCOMPARE(selectionModel.selectedIndexes().count(), 10); - QCOMPARE(selectionModel.selectedRows().count(), 10); + QCOMPARE(selectionModel.selectedIndexes().size(), 10); + QCOMPARE(selectionModel.selectedRows().size(), 10); + QVERIFY(selectionModel.hasSelection()); for (int j = 0; j < 10; ++j) model.item(j)->setFlags({ }); - QCOMPARE(selectionModel.selectedIndexes().count(), 0); - QCOMPARE(selectionModel.selectedRows().count(), 0); + QVERIFY(!selectionModel.hasSelection()); + QCOMPARE(selectionModel.selectedIndexes().size(), 0); + QCOMPARE(selectionModel.selectedRows().size(), 0); } void tst_QItemSelectionModel::selectedIndexes() @@ -2079,8 +2079,8 @@ void tst_QItemSelectionModel::selectedIndexes() //we select the 1st row selectionModel.select(selection, QItemSelectionModel::Rows | QItemSelectionModel::Select); - QCOMPARE(selectionModel.selectedRows().count(), 1); - QCOMPARE(selectionModel.selectedIndexes().count(), model.columnCount()); + QCOMPARE(selectionModel.selectedRows().size(), 1); + QCOMPARE(selectionModel.selectedIndexes().size(), model.columnCount()); } @@ -2088,18 +2088,18 @@ class QtTestTableModel: public QAbstractTableModel { Q_OBJECT public: - QtTestTableModel(int rows = 0, int columns = 0, QObject *parent = 0) + QtTestTableModel(int rows = 0, int columns = 0, QObject *parent = nullptr) : QAbstractTableModel(parent) , row_count(rows) , column_count(columns) { } - int rowCount(const QModelIndex& = QModelIndex()) const { return row_count; } - int columnCount(const QModelIndex& = QModelIndex()) const { return column_count; } + int rowCount(const QModelIndex& = QModelIndex()) const override { return row_count; } + int columnCount(const QModelIndex& = QModelIndex()) const override { return column_count; } bool isEditable(const QModelIndex &) const { return true; } - QVariant data(const QModelIndex &idx, int role) const + QVariant data(const QModelIndex &idx, int role) const override { if (role == Qt::DisplayRole || role == Qt::EditRole) return QLatin1Char('[') + QString::number(idx.row()) + QLatin1Char(',') @@ -2118,14 +2118,14 @@ void tst_QItemSelectionModel::layoutChanged() QtTestTableModel model(1,1); QItemSelectionModel selectionModel(&model); selectionModel.select(model.index(0,0), QItemSelectionModel::Select); - QCOMPARE(selectionModel.selectedIndexes().count() , 1); + QCOMPARE(selectionModel.selectedIndexes().size() , 1); emit model.layoutAboutToBeChanged(); model.row_count = 5; emit model.layoutChanged(); //The selection should not change. - QCOMPARE(selectionModel.selectedIndexes().count() , 1); + QCOMPARE(selectionModel.selectedIndexes().size() , 1); QCOMPARE(selectionModel.selectedIndexes().first() , model.index(0,0)); } @@ -2205,7 +2205,7 @@ void tst_QItemSelectionModel::isRowSelected() model.setData(model.index(0,0), 0, Qt::UserRole - 1); QItemSelectionModel sel(&model); sel.select( QItemSelection(model.index(0,0), model.index(0, 1)), QItemSelectionModel::Select); - QCOMPARE(sel.selectedIndexes().count(), 1); + QCOMPARE(sel.selectedIndexes().size(), 1); QVERIFY(sel.isRowSelected(0, QModelIndex())); } @@ -2230,7 +2230,7 @@ void tst_QItemSelectionModel::childrenDeselectionSignal() QSignalSpy deselectSpy(&selectionModel, &QItemSelectionModel::selectionChanged); QVERIFY(deselectSpy.isValid()); model.removeRows(0, 1, root); - QCOMPARE(deselectSpy.count(), 1); + QCOMPARE(deselectSpy.size(), 1); // More testing stress for the patch. model.clear(); @@ -2259,11 +2259,12 @@ void tst_QItemSelectionModel::childrenDeselectionSignal() QVERIFY(selectionModel.selection().contains(sel2)); deselectSpy.clear(); model.removeRow(0, model.index(0, 0)); - QCOMPARE(deselectSpy.count(), 1); + QCOMPARE(deselectSpy.size(), 1); QVERIFY(!selectionModel.selection().contains(sel)); QVERIFY(selectionModel.selection().contains(sel2)); } +#if QT_CONFIG(proxymodel) void tst_QItemSelectionModel::layoutChangedWithAllSelected1() { QStringListModel model( QStringList() << "foo" << "bar" << "foo2"); @@ -2281,7 +2282,7 @@ void tst_QItemSelectionModel::layoutChangedWithAllSelected1() selection.select(QItemSelection(indexList.first(), indexList.last()), QItemSelectionModel::Select); //let's check the selection hasn't changed - QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + QCOMPARE(selection.selectedIndexes().size(), indexList.size()); for (const auto &index : indexList) QVERIFY(selection.isSelected(index)); @@ -2289,7 +2290,7 @@ void tst_QItemSelectionModel::layoutChangedWithAllSelected1() QCOMPARE(proxy.rowCount(), 3); //let's check the selection hasn't changed - QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + QCOMPARE(selection.selectedIndexes().size(), indexList.size()); for (const auto &index : indexList) QVERIFY(selection.isSelected(index)); } @@ -2301,7 +2302,7 @@ void tst_QItemSelectionModel::layoutChangedWithAllSelected2() struct MyFilterModel : public QSortFilterProxyModel { // Override sort filter proxy to remove even numbered rows. bool filtering; - virtual bool filterAcceptsRow( int source_row, const QModelIndex& /* source_parent */) const + virtual bool filterAcceptsRow( int source_row, const QModelIndex& /* source_parent */) const override { return !filtering || !( source_row & 1 ); } @@ -2338,10 +2339,11 @@ void tst_QItemSelectionModel::layoutChangedWithAllSelected2() QCOMPARE(proxy.rowCount(), int(cNumRows)); //let's check the selection hasn't changed - QCOMPARE(selection.selectedIndexes().count(), indexList.count()); + QCOMPARE(selection.selectedIndexes().size(), indexList.size()); for (const auto &index : indexList) QVERIFY(selection.isSelected(index)); } +#endif // This test is a regression test for QTBUG-2804. void tst_QItemSelectionModel::layoutChangedTreeSelection() @@ -2362,11 +2364,11 @@ void tst_QItemSelectionModel::layoutChangedTreeSelection() selModel.select(sub23.index(), QItemSelectionModel::Select); QModelIndexList list = selModel.selectedIndexes(); - QCOMPARE(list.count(), 4); + QCOMPARE(list.size(), 4); model.sort(0); //this will provoke a relayout - QCOMPARE(selModel.selectedIndexes().count(), 4); + QCOMPARE(selModel.selectedIndexes().size(), 4); } class RemovalObserver : public QObject @@ -2417,165 +2419,6 @@ void tst_QItemSelectionModel::deselectRemovedMiddleRange() QCOMPARE(spy.size(), 1); } -static QStandardItemModel* getModel(QObject *parent) -{ - QStandardItemModel *model = new QStandardItemModel(parent); - - for (int i = 0; i < 4; ++i) { - QStandardItem *parentItem = model->invisibleRootItem(); - QList<QStandardItem*> list; - const QString prefix = QLatin1String("item ") + QString::number(i) + QLatin1String(", "); - for (int j = 0; j < 4; ++j) { - list.append(new QStandardItem(prefix + QString::number(j))); - } - parentItem->appendRow(list); - parentItem = list.first(); - for (int j = 0; j < 4; ++j) { - QList<QStandardItem*> list; - for (int k = 0; k < 4; ++k) { - list.append(new QStandardItem(prefix + QString::number(j))); - } - parentItem->appendRow(list); - } - } - return model; -} - -enum Result { - LessThan, - NotLessThan, - NotEqual -}; - -Q_DECLARE_METATYPE(Result); - -void tst_QItemSelectionModel::rangeOperatorLessThan_data() -{ - QTest::addColumn<int>("parent1"); - QTest::addColumn<int>("top1"); - QTest::addColumn<int>("left1"); - QTest::addColumn<int>("bottom1"); - QTest::addColumn<int>("right1"); - QTest::addColumn<int>("parent2"); - QTest::addColumn<int>("top2"); - QTest::addColumn<int>("left2"); - QTest::addColumn<int>("bottom2"); - QTest::addColumn<int>("right2"); - QTest::addColumn<Result>("result"); - - QTest::newRow("lt01") << -1 << 0 << 0 << 3 << 3 - << -1 << 0 << 0 << 3 << 3 << NotLessThan; - - QTest::newRow("lt02") << -1 << 0 << 0 << 2 << 3 - << -1 << 0 << 0 << 3 << 3 << LessThan; - QTest::newRow("lt03") << -1 << 0 << 0 << 3 << 2 - << -1 << 0 << 0 << 3 << 3 << LessThan; - QTest::newRow("lt04") << -1 << 0 << 0 << 2 << 2 - << -1 << 0 << 0 << 3 << 3 << LessThan; - - QTest::newRow("lt05") << -1 << 0 << 0 << 3 << 3 - << -1 << 0 << 0 << 2 << 3 << NotLessThan; - QTest::newRow("lt06") << -1 << 0 << 0 << 3 << 3 - << -1 << 0 << 0 << 3 << 2 << NotLessThan; - QTest::newRow("lt07") << -1 << 0 << 0 << 3 << 3 - << -1 << 0 << 0 << 2 << 2 << NotLessThan; - - QTest::newRow("lt08") << -1 << 0 << 0 << 3 << 3 - << 0 << 0 << 0 << 3 << 3 << NotEqual; - QTest::newRow("lt09") << 1 << 0 << 0 << 3 << 3 - << 0 << 0 << 0 << 3 << 3 << NotEqual; - QTest::newRow("lt10") << 1 << 0 << 0 << 1 << 1 - << 0 << 2 << 2 << 3 << 3 << NotEqual; - QTest::newRow("lt11") << 1 << 2 << 2 << 3 << 3 - << 0 << 0 << 0 << 1 << 1 << NotEqual; - - QTest::newRow("lt12") << -1 << 0 << 0 << 1 << 1 - << -1 << 2 << 2 << 3 << 3 << LessThan; - QTest::newRow("lt13") << -1 << 2 << 2 << 3 << 3 - << -1 << 0 << 0 << 1 << 1 << NotLessThan; - QTest::newRow("lt14") << 1 << 0 << 0 << 1 << 1 - << 1 << 2 << 2 << 3 << 3 << LessThan; - QTest::newRow("lt15") << 1 << 2 << 2 << 3 << 3 - << 1 << 0 << 0 << 1 << 1 << NotLessThan; - - QTest::newRow("lt16") << -1 << 0 << 0 << 2 << 2 - << -1 << 1 << 1 << 3 << 3 << LessThan; - QTest::newRow("lt17") << -1 << 1 << 1 << 3 << 3 - << -1 << 0 << 0 << 2 << 2 << NotLessThan; - QTest::newRow("lt18") << 1 << 0 << 0 << 2 << 2 - << 1 << 1 << 1 << 3 << 3 << LessThan; - QTest::newRow("lt19") << 1 << 1 << 1 << 3 << 3 - << 1 << 0 << 0 << 2 << 2 << NotLessThan; -} - -void tst_QItemSelectionModel::rangeOperatorLessThan() -{ - QStandardItemModel *model1 = getModel(this); - QStandardItemModel *model2 = getModel(this); - - QFETCH(int, parent1); - QFETCH(int, top1); - QFETCH(int, left1); - QFETCH(int, bottom1); - QFETCH(int, right1); - QFETCH(int, parent2); - QFETCH(int, top2); - QFETCH(int, left2); - QFETCH(int, bottom2); - QFETCH(int, right2); - QFETCH(Result, result); - - QModelIndex p1 = model1->index(parent1, 0); - - QModelIndex tl1 = model1->index(top1, left1, p1); - QModelIndex br1 = model1->index(bottom1, right1, p1); - - QItemSelectionRange r1(tl1, br1); - - QModelIndex p2 = model1->index(parent2, 0); - - QModelIndex tl2 = model1->index(top2, left2, p2); - QModelIndex br2 = model1->index(bottom2, right2, p2); - - QItemSelectionRange r2(tl2, br2); - - if (result == LessThan) - QVERIFY(r1 < r2); - else if (result == NotLessThan) - QVERIFY(!(r1 < r2)); - else if (result == NotEqual) - if (!(r1 < r2)) - QVERIFY(r2 < r1); - - // Ranges in different models are always non-equal - - QModelIndex p3 = model2->index(parent1, 0); - - QModelIndex tl3 = model2->index(top1, left1, p3); - QModelIndex br3 = model2->index(bottom1, right1, p3); - - QItemSelectionRange r3(tl3, br3); - - if (!(r1 < r3)) - QVERIFY(r3 < r1); - - if (!(r2 < r3)) - QVERIFY(r3 < r2); - - QModelIndex p4 = model2->index(parent2, 0); - - QModelIndex tl4 = model2->index(top2, left2, p4); - QModelIndex br4 = model2->index(bottom2, right2, p4); - - QItemSelectionRange r4(tl4, br4); - - if (!(r1 < r4)) - QVERIFY(r4 < r1); - - if (!(r2 < r4)) - QVERIFY(r4 < r2); -} - void tst_QItemSelectionModel::setModel() { QItemSelectionModel sel; @@ -2584,13 +2427,36 @@ void tst_QItemSelectionModel::setModel() QStringListModel model(QStringList() << "Blah" << "Blah" << "Blah"); sel.setModel(&model); QCOMPARE(sel.model(), &model); - QCOMPARE(modelChangedSpy.count(), 1); + QCOMPARE(modelChangedSpy.size(), 1); sel.select(model.index(0), QItemSelectionModel::Select); QVERIFY(!sel.selection().isEmpty()); sel.setModel(0); QVERIFY(sel.selection().isEmpty()); } +void tst_QItemSelectionModel::bindableModel() +{ + QItemSelectionModel sel; + QVERIFY(!sel.model()); + + std::unique_ptr<QStringListModel> firstModel( + new QStringListModel(QStringList { "Some", "random", "content" })); + std::unique_ptr<QStringListModel> changedModel( + new QStringListModel(QStringList { "Other", "random", "content" })); + + QTestPrivate::testReadWritePropertyBasics<QItemSelectionModel, QAbstractItemModel *>( + sel, firstModel.get(), changedModel.get(), "model"); + if (QTest::currentTestFailed()) { + qDebug("Failed property test for QItemSelectionModel::model"); + return; + } + + // check that model is set to nullptr when the object pointed to is deleted: + sel.setModel(firstModel.get()); + firstModel.reset(); + QCOMPARE(sel.model(), nullptr); +} + void tst_QItemSelectionModel::testDifferentModels() { QStandardItemModel model1; @@ -2620,7 +2486,7 @@ class SelectionObserver : public QObject { Q_OBJECT public: - SelectionObserver(QAbstractItemModel *model, QObject *parent = 0) + SelectionObserver(QAbstractItemModel *model, QObject *parent = nullptr) : QObject(parent), m_model(model), m_selectionModel(0) { connect(model, SIGNAL(modelReset()), SLOT(modelReset())); @@ -2684,12 +2550,12 @@ class DuplicateItemSelectionModel : public QItemSelectionModel { Q_OBJECT public: - DuplicateItemSelectionModel(QItemSelectionModel *target, QAbstractItemModel *model, QObject *parent = 0) + DuplicateItemSelectionModel(QItemSelectionModel *target, QAbstractItemModel *model, QObject *parent = nullptr) : QItemSelectionModel(model, parent), m_target(target) { } - void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) + void select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command) override { QItemSelectionModel::select(selection, command); m_target->select(selection, command); @@ -2697,13 +2563,13 @@ public: using QItemSelectionModel::select; - void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) + void setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command) override { QItemSelectionModel::setCurrentIndex(index, command); m_target->setCurrentIndex(index, command); } - void clearCurrentIndex() + void clearCurrentIndex() override { QItemSelectionModel::clearCurrentIndex(); m_target->clearCurrentIndex(); @@ -2861,6 +2727,9 @@ void tst_QItemSelectionModel::QTBUG48402() model.removeRows(removeTop, removeBottom - removeTop + 1); QCOMPARE(QItemSelectionRange(helper.tl, helper.br), QItemSelectionRange(dtl, dbr)); + QT_TEST_EQUALITY_OPS(QItemSelectionRange(helper.tl, helper.br), QItemSelectionRange(dtl, dbr), true); + QT_TEST_EQUALITY_OPS(QItemSelectionRange(), QItemSelectionRange(), true); + QT_TEST_EQUALITY_OPS(QItemSelectionRange(helper.tl, helper.br), QItemSelectionRange(), false); } void tst_QItemSelectionModel::QTBUG58851_data() @@ -2889,6 +2758,7 @@ void tst_QItemSelectionModel::QTBUG58851_data() << IntPair(2, 3)); } +#if QT_CONFIG(proxymodel) void tst_QItemSelectionModel::QTBUG58851() { using IntPair = std::pair<int, int>; @@ -2928,11 +2798,12 @@ void tst_QItemSelectionModel::QTBUG58851() QVERIFY(selections.isSelected(i)); } proxy.sort(1, Qt::DescendingOrder); - QCOMPARE(selections.selectedIndexes().count(), (int)expectedSelectedIndexes.size()); + QCOMPARE(selections.selectedIndexes().size(), (int)expectedSelectedIndexes.size()); for (const QPersistentModelIndex &i : expectedSelectedIndexes) { QVERIFY(selections.isSelected(i)); } } +#endif void tst_QItemSelectionModel::QTBUG18001_data() { @@ -3002,12 +2873,12 @@ void tst_QItemSelectionModel::QTBUG18001() QItemSelectionModel selectionModel(&model); - for (int i = 0; i < indexesToSelect.count(); ++i) { + for (int i = 0; i < indexesToSelect.size(); ++i) { QModelIndex idx = model.index( indexesToSelect.at(i).first, indexesToSelect.at(i).second ); selectionModel.select(idx, QItemSelectionModel::SelectionFlag(selectionCommands.at(i))); } - for (int i = 0; i < expectedSelectedRows.count(); ++i) { + for (int i = 0; i < expectedSelectedRows.size(); ++i) { const bool expected = expectedSelectedRows.at(i); const bool actual = selectionModel.isRowSelected(i, QModelIndex()); QByteArray description = QByteArray("Row ") + QByteArray::number(i) @@ -3016,7 +2887,7 @@ void tst_QItemSelectionModel::QTBUG18001() QVERIFY2(expected == actual, description.data()); } - for (int i = 0; i < expectedSelectedColums.count(); ++i) { + for (int i = 0; i < expectedSelectedColums.size(); ++i) { const bool expected = expectedSelectedColums.at(i); const bool actual = selectionModel.isColumnSelected(i, QModelIndex()); QByteArray description = QByteArray("Col ") + QByteArray::number(i) @@ -3027,5 +2898,94 @@ void tst_QItemSelectionModel::QTBUG18001() } +void tst_QItemSelectionModel::QTBUG93305() +{ + // make sure the model is sane (5x5) + QCOMPARE(model->rowCount(QModelIndex()), 5); + QCOMPARE(model->columnCount(QModelIndex()), 5); + + QSignalSpy spy(selection, &QItemSelectionModel::selectionChanged); + + // select in row 1 + QModelIndex index = model->index(1, 0, QModelIndex()); + selection->select(index, QItemSelectionModel::ClearAndSelect); + QVERIFY(selection->hasSelection()); + QCOMPARE(spy.size(), 1); + + // removing row 0 does not change which cells are selected, but it + // does change the row number of the selected cells. Thus it changes + // what selectedIndexes() returns. + // The property selectedIndexes() has selectionChanged() as its + // NOTIFY signal, so selectionChanged() should be emitted. + + // delete row 0 + model->removeRows(0, 1, QModelIndex()); + QVERIFY(selection->hasSelection()); + QCOMPARE(spy.size(), 2); + + // inserting a row before the first row again does not change which cells + // are selected, but does change the row number of the selected cells. + // This changes what selectedIndexes() returns and should thus trigger + // a selectionChanged() signal + + // insert row 0 again + model->insertRows(0, 1, QModelIndex()); + QVERIFY(selection->hasSelection()); + QCOMPARE(spy.size(), 3); + + // test for inserting multiple (6) rows + model->insertRows(0, 6, QModelIndex()); + QVERIFY(selection->hasSelection()); + QCOMPARE(spy.size(), 4); +} + +static void (*oldMessageHandler)(QtMsgType, const QMessageLogContext&, const QString&); +static bool signalError = false; + +// detect disconnect warning: +// qt.core.qobject.connect: QObject::disconnect: No such signal +void tst_QItemSelectionModel::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + Q_ASSERT(oldMessageHandler); + + if (type == QtWarningMsg + && QString(context.category) == "qt.core.qobject.connect" + && msg.contains("No such")) { + signalError = true; + } + + return oldMessageHandler(type, context, msg); +} + +void tst_QItemSelectionModel::testSignalsDisconnection() +{ + oldMessageHandler = qInstallMessageHandler(messageHandler); + auto resetMessageHandler = qScopeGuard([] { qInstallMessageHandler(oldMessageHandler); }); + auto *newModel = new QStandardItemModel(model); + selection->setModel(newModel); + QSignalSpy spy(newModel, &QObject::destroyed); + delete newModel; + QTRY_COMPARE(spy.count(), 1); + qDebug() << spy; + selection->setModel(nullptr); + QVERIFY(!signalError); +} + +void tst_QItemSelectionModel::destroyModel() +{ + auto itemModel = std::make_unique<QStandardItemModel>(5, 5); + auto selectionModel = std::make_unique<QItemSelectionModel>(); + selectionModel->setModel(itemModel.get()); + selectionModel->select(itemModel->index(0, 0), QItemSelectionModel::Select); + QVERIFY(!selectionModel->selection().isEmpty()); + selectionModel->setCurrentIndex(itemModel->index(1, 0), QItemSelectionModel::Select); + QVERIFY(selectionModel->currentIndex().isValid()); + + QTest::failOnWarning(); + itemModel.reset(); + QVERIFY(!selectionModel->currentIndex().isValid()); + QVERIFY(selectionModel->selection().isEmpty()); +} + QTEST_MAIN(tst_QItemSelectionModel) #include "tst_qitemselectionmodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/CMakeLists.txt new file mode 100644 index 0000000000..5427c52e78 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsortfilterproxymodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsortfilterproxymodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsortfilterproxymodel + SOURCES + ../../../other/qabstractitemmodelutils/dynamictreemodel.cpp ../../../other/qabstractitemmodelutils/dynamictreemodel.h + tst_qsortfilterproxymodel.cpp + INCLUDE_DIRECTORIES + ../../../other/qabstractitemmodelutils + LIBRARIES + Qt::Gui + Qt::Widgets + Qt::TestPrivate +) diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp index 7f9a996136..0e027461aa 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "tst_qsortfilterproxymodel.h" #include "dynamictreemodel.h" @@ -36,13 +11,17 @@ #include <QStringListModel> #include <QTableView> #include <QTreeView> -#include <QtTest> +#include <QTest> +#include <QStack> +#include <QSignalSpy> +#include <QAbstractItemModelTester> +#include <QtTest/private/qpropertytesthelper_p.h> Q_LOGGING_CATEGORY(lcItemModels, "qt.corelib.tests.itemmodels") using IntPair = QPair<int, int>; -using IntList = QVector<int>; -using IntPairList = QVector<IntPair>; +using IntList = QList<int>; +using IntPairList = QList<IntPair>; // Testing get/set functions void tst_QSortFilterProxyModel::getSetCheck() @@ -68,6 +47,7 @@ void tst_QSortFilterProxyModel::initTestCase() m_model = new QStandardItemModel(0, 1); m_proxy = new QSortFilterProxyModel(); m_proxy->setSourceModel(m_model); + new QAbstractItemModelTester(m_proxy, this); } void tst_QSortFilterProxyModel::cleanupTestCase() @@ -78,18 +58,11 @@ void tst_QSortFilterProxyModel::cleanupTestCase() void tst_QSortFilterProxyModel::cleanup() { - switch (m_filterType) { - case FilterType::RegExp: - m_proxy->setFilterRegExp(QRegExp()); - break; - case FilterType::RegularExpression: - m_proxy->setFilterRegularExpression(QRegularExpression()); - break; - } - + m_proxy->setFilterRegularExpression(QRegularExpression()); m_proxy->sort(-1, Qt::AscendingOrder); m_model->clear(); m_model->insertColumns(0, 1); + QCoreApplication::processEvents(); // cleanup possibly queued events } /* @@ -244,16 +217,16 @@ void tst_QSortFilterProxyModel::sort() // prepare model QStandardItem *root = m_model->invisibleRootItem (); QList<QStandardItem *> items; - for (int i = 0; i < initial.count(); ++i) { + for (int i = 0; i < initial.size(); ++i) { items.append(new QStandardItem(initial.at(i))); } root->insertRows(0, items); - QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.size()); QCOMPARE(m_model->columnCount(QModelIndex()), 1); // make sure the proxy is unsorted QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); - QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.count()); + QCOMPARE(m_proxy->rowCount(QModelIndex()), initial.size()); for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { QModelIndex index = m_proxy->index(row, 0, QModelIndex()); QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row)); @@ -274,13 +247,16 @@ void tst_QSortFilterProxyModel::sort() QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), expected.at(row)); } - // restore the unsorted order - m_proxy->sort(-1); + // restore the unsorted order in the given order + m_proxy->sort(-1, sortOrder); - // make sure the proxy is unsorted again + // make sure the proxy is sorted by source row in the given order + int sourceIndex = sortOrder == Qt::AscendingOrder ? 0 : initial.size() - 1; + int adjustmentValue = sortOrder == Qt::AscendingOrder ? 1 : -1; for (int row = 0; row < m_proxy->rowCount(QModelIndex()); ++row) { QModelIndex index = m_proxy->index(row, 0, QModelIndex()); - QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(row)); + QCOMPARE(m_proxy->data(index, Qt::DisplayRole).toString(), initial.at(sourceIndex)); + sourceIndex += adjustmentValue; } } @@ -441,10 +417,10 @@ void tst_QSortFilterProxyModel::insertRows() QFETCH(QStringList, insert); QFETCH(int, position); // prepare model - m_model->insertRows(0, initial.count(), QModelIndex()); + m_model->insertRows(0, initial.size(), QModelIndex()); //m_model->insertColumns(0, 1, QModelIndex()); QCOMPARE(m_model->columnCount(QModelIndex()), 1); - QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.size()); for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { QModelIndex index = m_model->index(row, 0, QModelIndex()); m_model->setData(index, initial.at(row), Qt::DisplayRole); @@ -461,12 +437,12 @@ void tst_QSortFilterProxyModel::insertRows() } // insert the row - m_proxy->insertRows(position, insert.count(), QModelIndex()); - QCOMPARE(m_model->rowCount(QModelIndex()), expected.count()); - QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); + m_proxy->insertRows(position, insert.size(), QModelIndex()); + QCOMPARE(m_model->rowCount(QModelIndex()), expected.size()); + QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.size()); // set the data for the inserted row - for (int i = 0; i < insert.count(); ++i) { + for (int i = 0; i < insert.size(); ++i) { QModelIndex index = m_proxy->index(position + i, 0, QModelIndex()); m_proxy->setData(index, insert.at(i), Qt::DisplayRole); } @@ -553,7 +529,7 @@ void tst_QSortFilterProxyModel::appendRowFromCombobox() QSortFilterProxyModel proxy; proxy.setSourceModel(&model); - proxy.setFilterRegExp(pattern); + proxy.setFilterRegularExpression(pattern); QComboBox comboBox; comboBox.setModel(&proxy); @@ -855,8 +831,8 @@ void tst_QSortFilterProxyModel::removeRows() // remove the rows QCOMPARE(proxy.removeRows(position, count, QModelIndex()), success); - QCOMPARE(model.rowCount(QModelIndex()), expectedSource.count()); - QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.count()); + QCOMPARE(model.rowCount(QModelIndex()), expectedSource.size()); + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxy.size()); // make sure the model is correct after remove for (int row = 0; row < model.rowCount(QModelIndex()); ++row) @@ -873,29 +849,16 @@ class MyFilteredColumnProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - MyFilteredColumnProxyModel(FilterType filterType, QObject *parent = nullptr) : - QSortFilterProxyModel(parent), - m_filterType(filterType) + MyFilteredColumnProxyModel(QObject *parent = nullptr) : + QSortFilterProxyModel(parent) { } protected: bool filterAcceptsColumn(int sourceColumn, const QModelIndex &) const override { QString key = sourceModel()->headerData(sourceColumn, Qt::Horizontal).toString(); - bool result = false; - switch (m_filterType) { - case FilterType::RegExp: - result = key.contains(filterRegExp()); - break; - case FilterType::RegularExpression: - result = key.contains(filterRegularExpression()); - break; - } - return result; + return key.contains(filterRegularExpression()); } - -private: - FilterType m_filterType; }; void tst_QSortFilterProxyModel::removeColumns_data() @@ -1104,7 +1067,7 @@ void tst_QSortFilterProxyModel::removeColumns() QFETCH(QStringList, expectedSource); QStandardItemModel model; - MyFilteredColumnProxyModel proxy(m_filterType); + MyFilteredColumnProxyModel proxy; proxy.setSourceModel(&model); if (!filter.isEmpty()) setupFilter(&proxy, filter); @@ -1114,8 +1077,8 @@ void tst_QSortFilterProxyModel::removeColumns() // remove the columns QCOMPARE(proxy.removeColumns(position, count, QModelIndex()), success); - QCOMPARE(model.columnCount(QModelIndex()), expectedSource.count()); - QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.count()); + QCOMPARE(model.columnCount(QModelIndex()), expectedSource.size()); + QCOMPARE(proxy.columnCount(QModelIndex()), expectedProxy.size()); // make sure the model is correct after remove for (int col = 0; col < model.columnCount(QModelIndex()); ++col) @@ -1141,7 +1104,6 @@ void tst_QSortFilterProxyModel::filterColumns_data() << "bravo" << "lima") << true; - QTest::newRow("some") << "lie" << (QStringList() << "charlie" @@ -1165,9 +1127,10 @@ void tst_QSortFilterProxyModel::filterColumns() QFETCH(QStringList, initial); QFETCH(bool, data); // prepare model - m_model->setColumnCount(initial.count()); + m_model->setColumnCount(initial.size()); m_model->setRowCount(1); - QCOMPARE(m_model->columnCount(QModelIndex()), initial.count()); + QCoreApplication::processEvents(); // QAbstractProxyModel queues the headerDataChanged() signal + QCOMPARE(m_model->columnCount(QModelIndex()), initial.size()); QCOMPARE(m_model->rowCount(QModelIndex()), 1); // set data QCOMPARE(m_model->rowCount(QModelIndex()), 1); @@ -1244,8 +1207,8 @@ void tst_QSortFilterProxyModel::filter() QFETCH(QStringList, expected); // prepare model - QVERIFY(m_model->insertRows(0, initial.count(), QModelIndex())); - QCOMPARE(m_model->rowCount(QModelIndex()), initial.count()); + QVERIFY(m_model->insertRows(0, initial.size(), QModelIndex())); + QCOMPARE(m_model->rowCount(QModelIndex()), initial.size()); // set data QCOMPARE(m_model->columnCount(QModelIndex()), 1); for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { @@ -1255,7 +1218,7 @@ void tst_QSortFilterProxyModel::filter() setupFilter(m_proxy, pattern); // make sure the proxy is unfiltered QCOMPARE(m_proxy->columnCount(QModelIndex()), 1); - QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.count()); + QCOMPARE(m_proxy->rowCount(QModelIndex()), expected.size()); // make sure the model is unchanged for (int row = 0; row < m_model->rowCount(QModelIndex()); ++row) { QModelIndex index = m_model->index(row, 0, QModelIndex()); @@ -1298,22 +1261,19 @@ void tst_QSortFilterProxyModel::filterHierarchy() void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractItemModel *m) { - int ind = 0; int row = 0; QStack<int> row_stack; QModelIndex parent; QStack<QModelIndex> parent_stack; - for (int i = 0; i < l.count(); ++i) { + for (int i = 0; i < l.size(); ++i) { QString token = l.at(i); if (token == QLatin1String("<")) { // start table - ++ind; parent_stack.push(parent); row_stack.push(row); parent = m->index(row - 1, 0, parent); row = 0; QVERIFY(m->insertColumns(0, 1, parent)); // add column } else if (token == QLatin1String(">")) { // end table - --ind; parent = parent_stack.pop(); row = row_stack.pop(); } else { // append row @@ -1329,21 +1289,18 @@ void tst_QSortFilterProxyModel::buildHierarchy(const QStringList &l, QAbstractIt void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbstractItemModel *m) { int row = 0; - int indent = 0; QStack<int> row_stack; QModelIndex parent; QStack<QModelIndex> parent_stack; - for (int i = 0; i < l.count(); ++i) { + for (int i = 0; i < l.size(); ++i) { QString token = l.at(i); if (token == QLatin1String("<")) { // start table - ++indent; parent_stack.push(parent); row_stack.push(row); parent = m->index(row - 1, 0, parent); QVERIFY(parent.isValid()); row = 0; } else if (token == QLatin1String(">")) { // end table - --indent; parent = parent_stack.pop(); row = row_stack.pop(); } else { // compare row @@ -1358,14 +1315,7 @@ void tst_QSortFilterProxyModel::checkHierarchy(const QStringList &l, const QAbst void tst_QSortFilterProxyModel::setupFilter(QSortFilterProxyModel *model, const QString& pattern) { - switch (m_filterType) { - case FilterType::RegExp: - model->setFilterRegExp(pattern); - break; - case FilterType::RegularExpression: - model->setFilterRegularExpression(pattern); - break; - } + model->setFilterRegularExpression(pattern); } class TestModel: public QAbstractTableModel @@ -1450,9 +1400,9 @@ void tst_QSortFilterProxyModel::filterCurrent() QVERIFY(spy.isValid()); view.setCurrentIndex(proxy.index(0, 0)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); setupFilter(&proxy, QLatin1String("^B")); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QSortFilterProxyModel::filter_qtbug30662() @@ -1490,8 +1440,8 @@ void tst_QSortFilterProxyModel::changeSourceLayout() QSortFilterProxyModel proxy; proxy.setSourceModel(&model); - QVector<QPersistentModelIndex> persistentSourceIndexes; - QVector<QPersistentModelIndex> persistentProxyIndexes; + QList<QPersistentModelIndex> persistentSourceIndexes; + QList<QPersistentModelIndex> persistentProxyIndexes; for (int row = 0; row < model.rowCount(); ++row) { persistentSourceIndexes.append(model.index(row, 0)); persistentProxyIndexes.append(proxy.index(row, 0)); @@ -1520,7 +1470,7 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut() // Filter everything out setupFilter(&proxy, QLatin1String("c")); - QCOMPARE(removeSpy.count(), 1); + QCOMPARE(removeSpy.size(), 1); QCOMPARE(0, proxy.rowCount()); // change layout of source model @@ -1530,7 +1480,7 @@ void tst_QSortFilterProxyModel::changeSourceLayoutFilteredOut() // Remove filter; we expect an insert setupFilter(&proxy, ""); - QCOMPARE(insertSpy.count(), 1); + QCOMPARE(insertSpy.size(), 1); QCOMPARE(beforeSortFilter, proxy.rowCount()); } @@ -1609,9 +1559,9 @@ void tst_QSortFilterProxyModel::removeSourceRows() proxy.setSourceModel(&model); model.insertColumns(0, 1); - model.insertRows(0, sourceItems.count()); + model.insertRows(0, sourceItems.size()); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex sindex = model.index(i, 0, QModelIndex()); model.setData(sindex, sourceItems.at(i), Qt::DisplayRole); QModelIndex pindex = proxy.index(i, 0, QModelIndex()); @@ -1634,28 +1584,28 @@ void tst_QSortFilterProxyModel::removeSourceRows() model.removeRows(start, count, QModelIndex()); - QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count()); - for (int i = 0; i < aboutToRemoveSpy.count(); ++i) { + QCOMPARE(aboutToRemoveSpy.size(), expectedRemovedProxyIntervals.size()); + for (int i = 0; i < aboutToRemoveSpy.size(); ++i) { const auto &args = aboutToRemoveSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); } - QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count()); - for (int i = 0; i < removeSpy.count(); ++i) { + QCOMPARE(removeSpy.size(), expectedRemovedProxyIntervals.size()); + for (int i = 0; i < removeSpy.size(); ++i) { const auto &args = removeSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); } - QCOMPARE(insertSpy.count(), 0); - QCOMPARE(aboutToInsertSpy.count(), 0); + QCOMPARE(insertSpy.size(), 0); + QCOMPARE(aboutToInsertSpy.size(), 0); - QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count()); - for (int i = 0; i < expectedProxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.size()); + for (int i = 0; i < expectedProxyItems.size(); ++i) { QModelIndex pindex = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i)); } @@ -1702,9 +1652,9 @@ void tst_QSortFilterProxyModel::insertSourceRows() proxy.setSourceModel(&model); model.insertColumns(0, 1); - model.insertRows(0, sourceItems.count()); + model.insertRows(0, sourceItems.size()); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex index = model.index(i, 0, QModelIndex()); model.setData(index, sourceItems.at(i), Qt::DisplayRole); } @@ -1714,13 +1664,13 @@ void tst_QSortFilterProxyModel::insertSourceRows() model.insertRows(start, newItems.size(), QModelIndex()); - QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count()); - for (int i = 0; i < newItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.size()); + for (int i = 0; i < newItems.size(); ++i) { QModelIndex index = model.index(start + i, 0, QModelIndex()); model.setData(index, newItems.at(i), Qt::DisplayRole); } - for (int i = 0; i < proxyItems.count(); ++i) { + for (int i = 0; i < proxyItems.size(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i)); } @@ -1793,9 +1743,9 @@ void tst_QSortFilterProxyModel::changeFilter() proxy.setSourceModel(&model); model.insertColumns(0, 1); - model.insertRows(0, sourceItems.count()); + model.insertRows(0, sourceItems.size()); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex index = model.index(i, 0, QModelIndex()); model.setData(index, sourceItems.at(i), Qt::DisplayRole); } @@ -1811,18 +1761,18 @@ void tst_QSortFilterProxyModel::changeFilter() setupFilter(&proxy, initialFilter); - QCOMPARE(initialRemoveSpy.count(), initialRemoveIntervals.count()); - QCOMPARE(initialInsertSpy.count(), 0); - for (int i = 0; i < initialRemoveSpy.count(); ++i) { + QCOMPARE(initialRemoveSpy.size(), initialRemoveIntervals.size()); + QCOMPARE(initialInsertSpy.size(), 0); + for (int i = 0; i < initialRemoveSpy.size(); ++i) { const auto &args = initialRemoveSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), initialRemoveIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), initialRemoveIntervals.at(i).second); } - QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.count()); - for (int i = 0; i < initialProxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), initialProxyItems.size()); + for (int i = 0; i < initialProxyItems.size(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initialProxyItems.at(i)); } @@ -1835,26 +1785,26 @@ void tst_QSortFilterProxyModel::changeFilter() setupFilter(&proxy, finalFilter); - QCOMPARE(finalRemoveSpy.count(), finalRemoveIntervals.count()); - for (int i = 0; i < finalRemoveSpy.count(); ++i) { + QCOMPARE(finalRemoveSpy.size(), finalRemoveIntervals.size()); + for (int i = 0; i < finalRemoveSpy.size(); ++i) { const auto &args = finalRemoveSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), finalRemoveIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), finalRemoveIntervals.at(i).second); } - QCOMPARE(finalInsertSpy.count(), insertIntervals.count()); - for (int i = 0; i < finalInsertSpy.count(); ++i) { + QCOMPARE(finalInsertSpy.size(), insertIntervals.size()); + for (int i = 0; i < finalInsertSpy.size(); ++i) { const auto &args = finalInsertSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second); } - QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.count()); - for (int i = 0; i < finalProxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), finalProxyItems.size()); + for (int i = 0; i < finalProxyItems.size(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), finalProxyItems.at(i)); } @@ -2017,9 +1967,9 @@ void tst_QSortFilterProxyModel::changeSourceData() proxy.setDynamicSortFilter(dynamic); proxy.setSourceModel(&model); model.insertColumns(0, 1); - model.insertRows(0, sourceItems.count()); + model.insertRows(0, sourceItems.size()); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex index = model.index(i, 0, QModelIndex()); model.setData(index, sourceItems.at(i), Qt::DisplayRole); } @@ -2029,8 +1979,8 @@ void tst_QSortFilterProxyModel::changeSourceData() setupFilter(&proxy, filter); - QCOMPARE(proxy.rowCount(), expectedInitialProxyItems.count()); - for (int i = 0; i < expectedInitialProxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(), expectedInitialProxyItems.size()); + for (int i = 0; i < expectedInitialProxyItems.size(); ++i) { const QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), expectedInitialProxyItems.at(i)); } @@ -2050,47 +2000,47 @@ void tst_QSortFilterProxyModel::changeSourceData() model.setData(index, newValue, Qt::DisplayRole); } - QCOMPARE(removeSpy.count(), removeIntervals.count()); - for (int i = 0; i < removeSpy.count(); ++i) { + QCOMPARE(removeSpy.size(), removeIntervals.size()); + for (int i = 0; i < removeSpy.size(); ++i) { const auto &args = removeSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), removeIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), removeIntervals.at(i).second); } - QCOMPARE(insertSpy.count(), insertIntervals.count()); - for (int i = 0; i < insertSpy.count(); ++i) { + QCOMPARE(insertSpy.size(), insertIntervals.size()); + for (int i = 0; i < insertSpy.size(); ++i) { const auto &args = insertSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), insertIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), insertIntervals.at(i).second); } - QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.count()); - for (int i = 0; i < proxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), proxyItems.size()); + for (int i = 0; i < proxyItems.size(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), proxyItems.at(i)); } if (expectedDataChangedRow == -1) { - QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.size(), 0); } else { - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); const QModelIndex idx = dataChangedSpy.at(0).at(0).value<QModelIndex>(); QCOMPARE(idx.row(), expectedDataChangedRow); QCOMPARE(idx.column(), 0); } - QCOMPARE(layoutChangedSpy.count(), expectedLayoutChanged ? 1 : 0); + QCOMPARE(layoutChangedSpy.size(), expectedLayoutChanged ? 1 : 0); } // Checks that the model is a table, and that each and every row is like this: // i-th row: ( rows.at(i), i ) static void checkSortedTableModel(const QAbstractItemModel *model, const QStringList &rows) { - QCOMPARE(model->rowCount(), rows.length()); + QCOMPARE(model->rowCount(), rows.size()); QCOMPARE(model->columnCount(), 2); for (int row = 0; row < model->rowCount(); ++row) { @@ -2114,7 +2064,7 @@ void tst_QSortFilterProxyModel::changeSourceDataKeepsStableSorting_qtbug1548() // Build a table of pairs (string, #row) in each row QStandardItemModel model(0, 2); - for (int rowNumber = 0; rowNumber < rows.length(); ++rowNumber) { + for (int rowNumber = 0; rowNumber < rows.size(); ++rowNumber) { QStandardItem *column0 = new QStandardItem(rows.at(rowNumber)); column0->setCheckable(true); column0->setCheckState(Qt::Unchecked); @@ -2168,27 +2118,256 @@ void tst_QSortFilterProxyModel::changeSourceDataForwardsRoles_qtbug35440() QSignalSpy spy(&proxy, &QAbstractItemModel::dataChanged); QVERIFY(spy.isValid()); - QCOMPARE(spy.length(), 0); + QCOMPARE(spy.size(), 0); QModelIndex index; // QStringListModel doesn't distinguish between edit and display roles, // so changing one always changes the other, too. - QVector<int> expectedChangedRoles; + QList<int> expectedChangedRoles; expectedChangedRoles.append(Qt::DisplayRole); expectedChangedRoles.append(Qt::EditRole); index = model.index(0, 0); QVERIFY(index.isValid()); model.setData(index, QStringLiteral("teststring"), Qt::DisplayRole); - QCOMPARE(spy.length(), 1); - QCOMPARE(spy.at(0).at(2).value<QVector<int> >(), expectedChangedRoles); + QCOMPARE(spy.size(), 1); + QCOMPARE(spy.at(0).at(2).value<QList<int> >(), expectedChangedRoles); index = model.index(1, 0); QVERIFY(index.isValid()); model.setData(index, QStringLiteral("teststring2"), Qt::EditRole); - QCOMPARE(spy.length(), 2); - QCOMPARE(spy.at(1).at(2).value<QVector<int> >(), expectedChangedRoles); + QCOMPARE(spy.size(), 2); + QCOMPARE(spy.at(1).at(2).value<QList<int> >(), expectedChangedRoles); +} + +void tst_QSortFilterProxyModel::changeSourceDataProxySendDataChanged_qtbug87781() +{ + QStandardItemModel baseModel; + QSortFilterProxyModel proxyModelBefore; + QSortFilterProxyModel proxyModelAfter; + + QSignalSpy baseDataChangedSpy(&baseModel, &QStandardItemModel::dataChanged); + QSignalSpy beforeDataChangedSpy(&proxyModelBefore, &QSortFilterProxyModel::dataChanged); + QSignalSpy afterDataChangedSpy(&proxyModelAfter, &QSortFilterProxyModel::dataChanged); + + QVERIFY(baseDataChangedSpy.isValid()); + QVERIFY(beforeDataChangedSpy.isValid()); + QVERIFY(afterDataChangedSpy.isValid()); + + proxyModelBefore.setSourceModel(&baseModel); + baseModel.insertRows(0, 1); + baseModel.insertColumns(0, 1); + proxyModelAfter.setSourceModel(&baseModel); + + QCOMPARE(baseDataChangedSpy.size(), 0); + QCOMPARE(beforeDataChangedSpy.size(), 0); + QCOMPARE(afterDataChangedSpy.size(), 0); + + baseModel.setData(baseModel.index(0, 0), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(baseDataChangedSpy.size(), 1); + QCOMPARE(beforeDataChangedSpy.size(), 1); + QCOMPARE(afterDataChangedSpy.size(), 1); +} + +void tst_QSortFilterProxyModel::changeSourceDataTreeModel() +{ + QStandardItemModel treeModel; + QSortFilterProxyModel treeProxyModelBefore; + QSortFilterProxyModel treeProxyModelAfter; + + QSignalSpy treeBaseDataChangedSpy(&treeModel, &QStandardItemModel::dataChanged); + QSignalSpy treeBeforeDataChangedSpy(&treeProxyModelBefore, &QSortFilterProxyModel::dataChanged); + QSignalSpy treeAfterDataChangedSpy(&treeProxyModelAfter, &QSortFilterProxyModel::dataChanged); + + QVERIFY(treeBaseDataChangedSpy.isValid()); + QVERIFY(treeBeforeDataChangedSpy.isValid()); + QVERIFY(treeAfterDataChangedSpy.isValid()); + + treeProxyModelBefore.setSourceModel(&treeModel); + QStandardItem treeNode1("data1"); + QStandardItem treeNode11("data11"); + QStandardItem treeNode111("data111"); + + treeNode1.appendRow(&treeNode11); + treeNode11.appendRow(&treeNode111); + treeModel.appendRow(&treeNode1); + treeProxyModelAfter.setSourceModel(&treeModel); + + QCOMPARE(treeBaseDataChangedSpy.size(), 0); + QCOMPARE(treeBeforeDataChangedSpy.size(), 0); + QCOMPARE(treeAfterDataChangedSpy.size(), 0); + + treeNode111.setData(QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(treeBaseDataChangedSpy.size(), 1); + QCOMPARE(treeBeforeDataChangedSpy.size(), 1); + QCOMPARE(treeAfterDataChangedSpy.size(), 1); +} + +void tst_QSortFilterProxyModel::changeSourceDataProxyFilterSingleColumn() +{ + enum modelRow { Row0, Row1, RowCount }; + enum modelColumn { Column0, Column1, Column2, Column3, Column4, Column5, ColumnCount }; + + class FilterProxyModel : public QSortFilterProxyModel + { + public: + bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override { + Q_UNUSED(source_parent); + switch (source_column) { + case Column2: + case Column4: + return true; + default: + return false; + } + } + }; + + QStandardItemModel model; + FilterProxyModel proxy; + proxy.setSourceModel(&model); + model.insertRows(0, RowCount); + model.insertColumns(0, ColumnCount); + + QSignalSpy modelDataChangedSpy(&model, &QSortFilterProxyModel::dataChanged); + QSignalSpy proxyDataChangedSpy(&proxy, &FilterProxyModel::dataChanged); + + QVERIFY(modelDataChangedSpy.isValid()); + QVERIFY(proxyDataChangedSpy.isValid()); + + modelDataChangedSpy.clear(); + proxyDataChangedSpy.clear(); + model.setData(model.index(Row0, Column1), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyDataChangedSpy.size(), 0); + + modelDataChangedSpy.clear(); + proxyDataChangedSpy.clear(); + model.setData(model.index(Row0, Column2), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyDataChangedSpy.size(), 1); + + modelDataChangedSpy.clear(); + proxyDataChangedSpy.clear(); + model.setData(model.index(Row0, Column3), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyDataChangedSpy.size(), 0); + + modelDataChangedSpy.clear(); + proxyDataChangedSpy.clear(); + model.setData(model.index(Row0, Column4), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyDataChangedSpy.size(), 1); + + modelDataChangedSpy.clear(); + proxyDataChangedSpy.clear(); + model.setData(model.index(Row0, Column5), QStringLiteral("new data"), Qt::DisplayRole); + QCOMPARE(modelDataChangedSpy.size(), 1); + QCOMPARE(proxyDataChangedSpy.size(), 0); +} + +void tst_QSortFilterProxyModel::changeSourceDataProxyFilterMultipleColumns() +{ + class FilterProxyModel : public QSortFilterProxyModel + { + public: + bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override { + Q_UNUSED(source_parent); + switch (source_column) { + case 2: + case 4: + return true; + default: + return false; + } + } + }; + + class MyTableModel : public QAbstractTableModel + { + public: + explicit MyTableModel() = default; + int rowCount(const QModelIndex &parent = QModelIndex()) const override { + Q_UNUSED(parent) + return 10; + } + int columnCount(const QModelIndex &parent = QModelIndex()) const override { + Q_UNUSED(parent) + return 10; + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { + Q_UNUSED(index) + Q_UNUSED(role) + return QString("testData"); + } + + void testDataChanged(const int topLeftRow, const int topLeftColumn, const int bottomRightRow, const int bottomRightColumn) { + QModelIndex topLeft = index(topLeftRow, topLeftColumn); + QModelIndex bottomRight = index(bottomRightRow, bottomRightColumn); + QVERIFY(topLeft.isValid()); + QVERIFY(bottomRight.isValid()); + emit dataChanged(topLeft, bottomRight); + } + }; + + MyTableModel baseModel; + FilterProxyModel proxyModel; + + proxyModel.setSourceModel(&baseModel); + + QSignalSpy baseModelDataChangedSpy(&baseModel, &MyTableModel::dataChanged); + QSignalSpy proxyModelDataChangedSpy(&proxyModel, &FilterProxyModel::dataChanged); + + connect(&proxyModel, &FilterProxyModel::dataChanged, [=](const QModelIndex &topLeft, const QModelIndex &bottomRight) { + QVERIFY(topLeft.isValid()); + QVERIFY(bottomRight.isValid()); + + //make sure every element is valid + int topLeftRow = topLeft.row(); + int topLeftColumn = topLeft.column(); + int bottomRightRow = bottomRight.row(); + int bottomRightColumn = bottomRight.column(); + for (int row = topLeftRow; row <= bottomRightRow; ++row) { + for (int column = topLeftColumn; column <= bottomRightColumn; ++column) { + QModelIndex index = topLeft.model()->index(row, column); + QVERIFY(index.isValid()); + } + } + }); + + QVERIFY(baseModelDataChangedSpy.isValid()); + QVERIFY(proxyModelDataChangedSpy.isValid()); + + baseModelDataChangedSpy.clear(); + proxyModelDataChangedSpy.clear(); + baseModel.testDataChanged(0, 0, 1, 1); + QCOMPARE(baseModelDataChangedSpy.size(), 1); + QCOMPARE(proxyModelDataChangedSpy.size(), 0); + + baseModelDataChangedSpy.clear(); + proxyModelDataChangedSpy.clear(); + baseModel.testDataChanged(0, 0, 1, 2); + QCOMPARE(baseModelDataChangedSpy.size(), 1); + QCOMPARE(proxyModelDataChangedSpy.size(), 1); + + baseModelDataChangedSpy.clear(); + proxyModelDataChangedSpy.clear(); + baseModel.testDataChanged(0, 3, 1, 3); + QCOMPARE(baseModelDataChangedSpy.size(), 1); + QCOMPARE(proxyModelDataChangedSpy.size(), 0); + + baseModelDataChangedSpy.clear(); + proxyModelDataChangedSpy.clear(); + baseModel.testDataChanged(0, 3, 1, 5); + QCOMPARE(baseModelDataChangedSpy.size(), 1); + QCOMPARE(proxyModelDataChangedSpy.size(), 1); + + baseModelDataChangedSpy.clear(); + proxyModelDataChangedSpy.clear(); + baseModel.testDataChanged(0, 0, 1, 5); + QCOMPARE(baseModelDataChangedSpy.size(), 1); + QCOMPARE(proxyModelDataChangedSpy.size(), 1); } void tst_QSortFilterProxyModel::sortFilterRole() @@ -2198,15 +2377,15 @@ void tst_QSortFilterProxyModel::sortFilterRole() proxy.setSourceModel(&model); model.insertColumns(0, 1); - const QVector<QPair<QVariant, QVariant>> + const QList<QPair<QVariant, QVariant>> sourceItems({QPair<QVariant, QVariant>("b", 3), QPair<QVariant, QVariant>("c", 2), QPair<QVariant, QVariant>("a", 1)}); - const QVector<int> orderedItems({2, 1}); + const QList<int> orderedItems({2, 1}); - model.insertRows(0, sourceItems.count()); - for (int i = 0; i < sourceItems.count(); ++i) { + model.insertRows(0, sourceItems.size()); + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex index = model.index(i, 0, QModelIndex()); model.setData(index, sourceItems.at(i).first, Qt::DisplayRole); model.setData(index, sourceItems.at(i).second, Qt::UserRole); @@ -2236,7 +2415,7 @@ void tst_QSortFilterProxyModel::sortFilterRole() proxy.setFilterRole(Qt::DisplayRole); setupFilter(&proxy, QLatin1String("a|c")); - QCOMPARE(proxy.rowCount(), orderedItems.count()); + QCOMPARE(proxy.rowCount(), orderedItems.size()); for (int i = 0; i < proxy.rowCount(); ++i) { QModelIndex index = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole), sourceItems.at(orderedItems.at(i)).first); @@ -2258,10 +2437,10 @@ void tst_QSortFilterProxyModel::selectionFilteredOut() QVERIFY(spy.isValid()); view.setCurrentIndex(proxy.index(0, 0)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); setupFilter(&proxy, QLatin1String("^B")); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); } void tst_QSortFilterProxyModel::match_data() @@ -2338,9 +2517,9 @@ void tst_QSortFilterProxyModel::match() proxy.setSourceModel(&model); model.insertColumns(0, 1); - model.insertRows(0, sourceItems.count()); + model.insertRows(0, sourceItems.size()); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex index = model.index(i, 0, QModelIndex()); model.setData(index, sourceItems.at(i), Qt::DisplayRole); } @@ -2350,10 +2529,10 @@ void tst_QSortFilterProxyModel::match() QModelIndex startIndex = proxy.index(proxyStartRow, 0); QModelIndexList indexes = proxy.match(startIndex, Qt::DisplayRole, what, - expectedProxyItems.count(), + expectedProxyItems.size(), matchFlags); - QCOMPARE(indexes.count(), expectedProxyItems.count()); - for (int i = 0; i < indexes.count(); ++i) + QCOMPARE(indexes.size(), expectedProxyItems.size()); + for (int i = 0; i < indexes.size(); ++i) QCOMPARE(indexes.at(i).row(), expectedProxyItems.at(i)); } @@ -2416,13 +2595,13 @@ void tst_QSortFilterProxyModel::insertIntoChildrenlessItem() QVERIFY(rowsInsertedSpy.isValid()); (void)proxy.rowCount(QModelIndex()); // force mapping of "a", "b", "c" - QCOMPARE(colsInsertedSpy.count(), 0); - QCOMPARE(rowsInsertedSpy.count(), 0); + QCOMPARE(colsInsertedSpy.size(), 0); + QCOMPARE(rowsInsertedSpy.size(), 0); // now add a child to itemB ==> should get insert notification from the proxy itemB->appendRow(new QStandardItem("a.0")); - QCOMPARE(colsInsertedSpy.count(), 1); - QCOMPARE(rowsInsertedSpy.count(), 1); + QCOMPARE(colsInsertedSpy.size(), 1); + QCOMPARE(rowsInsertedSpy.size(), 1); QVariantList args = colsInsertedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), proxy.mapFromSource(itemB->index())); @@ -2498,11 +2677,11 @@ void tst_QSortFilterProxyModel::insertRowIntoFilteredParent() QStandardItem *itemB = new QStandardItem(); itemA->appendRow(itemB); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); itemA->removeRow(0); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild() @@ -2529,9 +2708,9 @@ void tst_QSortFilterProxyModel::filterOutParentAndFilterInChild() setupFilter(&proxy, QLatin1String("C")); // A and B will be filtered out, C filtered in // we should now have been notified that the subtree represented by itemA has been removed - QCOMPARE(removedSpy.count(), 1); + QCOMPARE(removedSpy.size(), 1); // we should NOT get any inserts; itemC should be filtered because its parent (itemA) is - QCOMPARE(insertedSpy.count(), 0); + QCOMPARE(insertedSpy.size(), 0); } void tst_QSortFilterProxyModel::sourceInsertRows() @@ -2624,13 +2803,13 @@ void tst_QSortFilterProxyModel::sortColumnTracking2() model.insertColumn(0,items); QCOMPARE(proxyModel.sortColumn(), 0); QCOMPARE(proxyModel.data(proxyModel.index(0,0)).toString(),QString::fromLatin1("aa")); - const int zzIndex = items.count() - 3; // 2 invalid at end. + const int zzIndex = items.size() - 3; // 2 invalid at end. QCOMPARE(proxyModel.data(proxyModel.index(zzIndex,0)).toString(),QString::fromLatin1("zz")); } void tst_QSortFilterProxyModel::sortStable() { - QStandardItemModel* model = new QStandardItemModel(5, 2); + QStandardItemModel model(5, 2); for (int r = 0; r < 5; r++) { const QString prefix = QLatin1String("Row:") + QString::number(r) + QLatin1String(", Column:"); for (int c = 0; c < 2; c++) { @@ -2639,24 +2818,24 @@ void tst_QSortFilterProxyModel::sortStable() QStandardItem* child = new QStandardItem(QLatin1String("Item ") + QString::number(i)); item->appendRow( child ); } - model->setItem(r, c, item); + model.setItem(r, c, item); } } - model->setHorizontalHeaderItem( 0, new QStandardItem( "Name" )); - model->setHorizontalHeaderItem( 1, new QStandardItem( "Value" )); + model.setHorizontalHeaderItem( 0, new QStandardItem( "Name" )); + model.setHorizontalHeaderItem( 1, new QStandardItem( "Value" )); - QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(model); - filterModel->setSourceModel(model); + QSortFilterProxyModel *filterModel = new QSortFilterProxyModel(&model); + filterModel->setSourceModel(&model); - QTreeView *view = new QTreeView; - view->setModel(filterModel); + QTreeView view; + view.setModel(filterModel); QModelIndex firstRoot = filterModel->index(0,0); - view->expand(firstRoot); - view->setSortingEnabled(true); + view.expand(firstRoot); + view.setSortingEnabled(true); - view->model()->sort(1, Qt::DescendingOrder); + view.model()->sort(1, Qt::DescendingOrder); QVariant lastItemData =filterModel->index(2,0, firstRoot).data(); - view->model()->sort(1, Qt::DescendingOrder); + view.model()->sort(1, Qt::DescendingOrder); QCOMPARE(lastItemData, filterModel->index(2,0, firstRoot).data()); } @@ -2716,16 +2895,16 @@ void tst_QSortFilterProxyModel::staticSorting() // prepare model QStandardItem *root = model.invisibleRootItem (); QList<QStandardItem *> items; - for (int i = 0; i < initial.count(); ++i) { + for (int i = 0; i < initial.size(); ++i) { items.append(new QStandardItem(initial.at(i))); } root->insertRows(0, items); - QCOMPARE(model.rowCount(QModelIndex()), initial.count()); + QCOMPARE(model.rowCount(QModelIndex()), initial.size()); QCOMPARE(model.columnCount(QModelIndex()), 1); // make sure the proxy is unsorted QCOMPARE(proxy.columnCount(QModelIndex()), 1); - QCOMPARE(proxy.rowCount(QModelIndex()), initial.count()); + QCOMPARE(proxy.rowCount(QModelIndex()), initial.size()); for (int row = 0; row < proxy.rowCount(QModelIndex()); ++row) { QModelIndex index = proxy.index(row, 0, QModelIndex()); QCOMPARE(proxy.data(index, Qt::DisplayRole).toString(), initial.at(row)); @@ -2775,7 +2954,7 @@ void tst_QSortFilterProxyModel::dynamicSorting() QCOMPARE(proxy1.columnCount(QModelIndex()), 1); //the model should not be sorted because sorting has not been set to dynamic yet. - QCOMPARE(proxy1.rowCount(QModelIndex()), initial.count()); + QCOMPARE(proxy1.rowCount(QModelIndex()), initial.size()); for (int row = 0; row < proxy1.rowCount(QModelIndex()); ++row) { QModelIndex index = proxy1.index(row, 0, QModelIndex()); QCOMPARE(proxy1.data(index, Qt::DisplayRole).toString(), initial.at(row)); @@ -2851,7 +3030,7 @@ public: bool hasChildren(const QModelIndex &parent = QModelIndex()) const override { - Q_UNUSED(parent) + Q_UNUSED(parent); return true; } @@ -2862,7 +3041,7 @@ public: int columnCount(const QModelIndex& parent = QModelIndex()) const override { - Q_UNUSED(parent) + Q_UNUSED(parent); return cols; } @@ -2991,7 +3170,8 @@ void tst_QSortFilterProxyModel::mapFromToSource() QCOMPARE(proxy.mapFromSource(QModelIndex()), QModelIndex()); QCOMPARE(proxy.mapToSource(QModelIndex()), QModelIndex()); -#ifdef QT_NO_DEBUG //if Qt is compiled in debug mode, this will assert + // Will assert in debug, so only test in release +#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapToSource"); QCOMPARE(proxy.mapToSource(source.index(2, 3)), QModelIndex()); QTest::ignoreMessage(QtWarningMsg, "QSortFilterProxyModel: index from wrong model passed to mapFromSource"); @@ -3032,25 +3212,25 @@ void tst_QSortFilterProxyModel::removeRowsRecursive() QList<QPersistentModelIndex> sourceIndexes; QList<QPersistentModelIndex> proxyIndexes; - for (const auto item : qAsConst(items)) { + for (const auto item : std::as_const(items)) { QModelIndex idx = item->index(); sourceIndexes << idx; proxyIndexes << proxy.mapFromSource(idx); } - for (const auto &pidx : qAsConst(sourceIndexes)) + for (const auto &pidx : std::as_const(sourceIndexes)) QVERIFY(pidx.isValid()); - for (const auto &pidx : qAsConst(proxyIndexes)) + for (const auto &pidx : std::as_const(proxyIndexes)) QVERIFY(pidx.isValid()); QList<QStandardItem*> itemRow = pItem1->takeRow(0); - QCOMPARE(itemRow.count(), 1); + QCOMPARE(itemRow.size(), 1); QCOMPARE(itemRow.first(), pItem11); - for (const auto &pidx : qAsConst(sourceIndexes)) + for (const auto &pidx : std::as_const(sourceIndexes)) QVERIFY(!pidx.isValid()); - for (const auto &pidx : qAsConst(proxyIndexes)) + for (const auto &pidx : std::as_const(proxyIndexes)) QVERIFY(!pidx.isValid()); delete pItem11; @@ -3058,36 +3238,36 @@ void tst_QSortFilterProxyModel::removeRowsRecursive() void tst_QSortFilterProxyModel::doubleProxySelectionSetSourceModel() { - QStandardItemModel *model1 = new QStandardItemModel; - QStandardItem *parentItem = model1->invisibleRootItem(); + QStandardItemModel model1; + QStandardItem *parentItem = model1.invisibleRootItem(); for (int i = 0; i < 4; ++i) { QStandardItem *item = new QStandardItem(QLatin1String("model1 item ") + QString::number(i)); parentItem->appendRow(item); parentItem = item; } - QStandardItemModel *model2 = new QStandardItemModel; - QStandardItem *parentItem2 = model2->invisibleRootItem(); + QStandardItemModel model2; + QStandardItem *parentItem2 = model2.invisibleRootItem(); for (int i = 0; i < 4; ++i) { QStandardItem *item = new QStandardItem(QLatin1String("model2 item ") + QString::number(i)); parentItem2->appendRow(item); parentItem2 = item; } - QSortFilterProxyModel *toggleProxy = new QSortFilterProxyModel; - toggleProxy->setSourceModel(model1); + QSortFilterProxyModel toggleProxy; + toggleProxy.setSourceModel(&model1); - QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel; - proxyModel->setSourceModel(toggleProxy); + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&toggleProxy); - QModelIndex mi = proxyModel->index(0, 0, proxyModel->index(0, 0, proxyModel->index(0, 0))); - QItemSelectionModel ism(proxyModel); + QModelIndex mi = proxyModel.index(0, 0, proxyModel.index(0, 0, proxyModel.index(0, 0))); + QItemSelectionModel ism(&proxyModel); ism.select(mi, QItemSelectionModel::Select); QModelIndexList mil = ism.selectedIndexes(); - QCOMPARE(mil.count(), 1); + QCOMPARE(mil.size(), 1); QCOMPARE(mil.first(), mi); - toggleProxy->setSourceModel(model2); + toggleProxy.setSourceModel(&model2); // No crash, it's good news! QVERIFY(ism.selection().isEmpty()); } @@ -3155,10 +3335,10 @@ void tst_QSortFilterProxyModel::appearsAndSort() QCOMPARE(secondProxyModel.data(secondProxyModel.index(1,0), Qt::DisplayRole).toString(), QString::fromLatin1("b")); QCOMPARE(secondProxyModel.data(secondProxyModel.index(2,0), Qt::DisplayRole).toString(), QString::fromLatin1("a")); - QCOMPARE(spyAbout1.count(), 1); - QCOMPARE(spyChanged1.count(), 1); - QCOMPARE(spyAbout2.count(), 1); - QCOMPARE(spyChanged2.count(), 1); + QCOMPARE(spyAbout1.size(), 1); + QCOMPARE(spyChanged1.size(), 1); + QCOMPARE(spyAbout2.size(), 1); + QCOMPARE(spyChanged2.size(), 1); } void tst_QSortFilterProxyModel::unnecessaryDynamicSorting() @@ -3528,7 +3708,7 @@ public: } private slots: - void resetInternalData() + void resetInternalData() override { m_backgroundColours.clear(); } @@ -4014,14 +4194,14 @@ public: using QAbstractListModel::QAbstractListModel; QVariant data(const QModelIndex &index, int role) const override { - Q_UNUSED(index) - Q_UNUSED(role) + Q_UNUSED(index); + Q_UNUSED(role); return QVariant(); } int rowCount(const QModelIndex &parent = QModelIndex()) const override { - Q_UNUSED(parent) + Q_UNUSED(parent); return 0; } @@ -4062,15 +4242,15 @@ public: int rowCount(const QModelIndex &parent = QModelIndex()) const override { - Q_UNUSED(parent) + Q_UNUSED(parent); return 10; } bool canDropMimeData(const QMimeData *, Qt::DropAction, int row, int column, const QModelIndex &parent) const override { - Q_UNUSED(row) - Q_UNUSED(column) + Q_UNUSED(row); + Q_UNUSED(column); return parent.row() % 2 == 0; } }; @@ -4465,7 +4645,7 @@ public: return lst.at(index.row()); return QVariant(); } - int rowCount(const QModelIndex & = QModelIndex()) const override { return lst.count(); } + int rowCount(const QModelIndex & = QModelIndex()) const override { return lst.size(); } void replaceData(const QStringList &newData) { @@ -4606,7 +4786,7 @@ void tst_QSortFilterProxyModel::removeIntervals() model.replaceData(sourceItems); proxy.setSourceModel(&model); - for (int i = 0; i < sourceItems.count(); ++i) { + for (int i = 0; i < sourceItems.size(); ++i) { QModelIndex sindex = model.index(i, 0, QModelIndex()); QModelIndex pindex = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(pindex, Qt::DisplayRole), model.data(sindex, Qt::DisplayRole)); @@ -4631,28 +4811,28 @@ void tst_QSortFilterProxyModel::removeIntervals() model.replaceData(replacementSourceItems); - QCOMPARE(aboutToRemoveSpy.count(), expectedRemovedProxyIntervals.count()); - for (int i = 0; i < aboutToRemoveSpy.count(); ++i) { + QCOMPARE(aboutToRemoveSpy.size(), expectedRemovedProxyIntervals.size()); + for (int i = 0; i < aboutToRemoveSpy.size(); ++i) { const auto &args = aboutToRemoveSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); } - QCOMPARE(removeSpy.count(), expectedRemovedProxyIntervals.count()); - for (int i = 0; i < removeSpy.count(); ++i) { + QCOMPARE(removeSpy.size(), expectedRemovedProxyIntervals.size()); + for (int i = 0; i < removeSpy.size(); ++i) { const auto &args = removeSpy.at(i); - QCOMPARE(args.at(1).type(), QVariant::Int); - QCOMPARE(args.at(2).type(), QVariant::Int); + QCOMPARE(args.at(1).userType(), QMetaType::Int); + QCOMPARE(args.at(2).userType(), QMetaType::Int); QCOMPARE(args.at(1).toInt(), expectedRemovedProxyIntervals.at(i).first); QCOMPARE(args.at(2).toInt(), expectedRemovedProxyIntervals.at(i).second); } - QCOMPARE(insertSpy.count(), 0); - QCOMPARE(aboutToInsertSpy.count(), 0); + QCOMPARE(insertSpy.size(), 0); + QCOMPARE(aboutToInsertSpy.size(), 0); - QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.count()); - for (int i = 0; i < expectedProxyItems.count(); ++i) { + QCOMPARE(proxy.rowCount(QModelIndex()), expectedProxyItems.size()); + for (int i = 0; i < expectedProxyItems.size(); ++i) { QModelIndex pindex = proxy.index(i, 0, QModelIndex()); QCOMPARE(proxy.data(pindex, Qt::DisplayRole).toString(), expectedProxyItems.at(i)); } @@ -4679,7 +4859,7 @@ void tst_QSortFilterProxyModel::dynamicFilterWithoutSort() QCOMPARE(model.stringList(), QStringList() << "Monday" << "Tuesday" << "Wednesday" << "Thursday" << "Friday"); - QCOMPARE(resetSpy.count(), 1); + QCOMPARE(resetSpy.size(), 1); } void tst_QSortFilterProxyModel::checkSetNewModel() @@ -4799,7 +4979,7 @@ void tst_QSortFilterProxyModel::filterAndInsertColumn() {} bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override { - Q_UNUSED(source_parent) + Q_UNUSED(source_parent); switch (filerMode){ case FilterAll: return true; @@ -4822,7 +5002,7 @@ void tst_QSortFilterProxyModel::filterAndInsertColumn() model.insertRows(0, 1); for (int i = 0; i < model.rowCount(); ++i) { for (int j = 0; j < model.columnCount(); ++j) - model.setData(model.index(i, j), QString('A' + j) + QString::number(i + 1)); + model.setData(model.index(i, j), QString(QChar('A' + j)) + QString::number(i + 1)); } ColumnFilterProxy proxy(filterMode); proxy.setSourceModel(&model); @@ -4928,11 +5108,11 @@ void tst_QSortFilterProxyModel::filterAndInsertRow() model.setStringList(initialModelList); proxyModel.setSourceModel(&model); proxyModel.setDynamicSortFilter(true); - proxyModel.setFilterRegExp(filterRegExp); + proxyModel.setFilterRegularExpression(filterRegExp); QVERIFY(proxyModel.insertRow(row)); QCOMPARE(model.stringList(), expectedModelList); - QCOMPARE(proxyModel.rowCount(), expectedProxyModelList.count()); + QCOMPARE(proxyModel.rowCount(), expectedProxyModelList.size()); for (int r = 0; r < proxyModel.rowCount(); ++r) { QModelIndex index = proxyModel.index(r, 0); QVERIFY(index.isValid()); @@ -4940,4 +5120,382 @@ void tst_QSortFilterProxyModel::filterAndInsertRow() } } + +namespace CheckFilteredIndexes { +class TableModel : public QAbstractTableModel +{ + Q_OBJECT +public: + static const int s_rowCount = 1000; + static const int s_columnCount = 1; + TableModel(QObject *parent = nullptr) : QAbstractTableModel(parent) + { + m_data.resize(s_rowCount); + for (int r = 0; r < s_rowCount; ++r) { + auto &curRow = m_data[r]; + curRow.resize(s_columnCount); + for (int c = 0; c < s_columnCount; ++c) { + curRow[c] = QVariant(QString::number(r % 100) + + QLatin1Char('_') + QString::number(r / 100) + + QString::number(c)); + } + } + } + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + return parent.isValid() ? 0 : s_rowCount; + } + int columnCount(const QModelIndex &parent = QModelIndex()) const override + { + return parent.isValid() ? 0 : s_columnCount; + } + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + if (role != Qt::DisplayRole || !index.isValid()) + return QVariant(); + return m_data[index.row()][index.column()]; + } + QList<QList<QVariant>> m_data; +}; + +class SortFilterProxyModel final : public QSortFilterProxyModel +{ + Q_OBJECT +public: + using QSortFilterProxyModel::QSortFilterProxyModel; + using QSortFilterProxyModel::invalidateFilter; + + void setSourceModel(QAbstractItemModel *m) override + { + m_sourceModel = qobject_cast<TableModel*>(m); + QSortFilterProxyModel::setSourceModel(m); + } + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override + { + if (!left.isValid() || !right.isValid() || !m_sourceModel) + return QSortFilterProxyModel::lessThan(left, right); + return m_sourceModel->m_data.at(left.row()).at(left.column()).toString() < + m_sourceModel->m_data.at(right.row()).at(right.column()).toString(); + } + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override + { + if (m_filteredRows == 0 || source_parent.isValid()) + return true; + return (source_row % m_filteredRows) != 0; + } + + TableModel *m_sourceModel = nullptr; + int m_filteredRows = 0; + bool m_bInverseFilter = false; +}; +} + +void tst_QSortFilterProxyModel::checkFilteredIndexes() +{ + using namespace CheckFilteredIndexes; + auto checkIndexes = [](const SortFilterProxyModel &s) + { + for (int r = 0; r < s.rowCount(); ++r) { + const QModelIndex idxSFPM = s.index(r, 0); + const QModelIndex idxTM = s.mapToSource(idxSFPM); + QVERIFY(idxTM.isValid()); + QVERIFY(idxTM.row() % s.m_filteredRows != 0); + } + }; + + TableModel m; + SortFilterProxyModel s; + s.m_filteredRows = 2; // every 2nd row is filtered + s.setSourceModel(&m); + s.sort(0); + + s.invalidateFilter(); + checkIndexes(s); + + s.m_filteredRows = 5; // every 5th row is filtered + s.invalidateFilter(); + checkIndexes(s); + + s.m_filteredRows = 3; // every 3rd row is filtered + s.invalidateFilter(); + checkIndexes(s); +} + +void tst_QSortFilterProxyModel::invalidateColumnsOrRowsFilter() +{ + class FilterProxy : public QSortFilterProxyModel + { + public: + FilterProxy() + {} + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override + { + rowFiltered++; + + if (sourceModel()->data(sourceModel()->index(source_row, 0, source_parent)).toString() == QLatin1String("A1")) + return !rejectA1; + return true; + } + bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const override + { + Q_UNUSED(source_column); + Q_UNUSED(source_parent); + + columnFiltered++; + return true; + } + + mutable int rowFiltered = 0; + mutable int columnFiltered = 0; + bool rejectA1 = false; + + using QSortFilterProxyModel::invalidateFilter; + using QSortFilterProxyModel::invalidateRowsFilter; + using QSortFilterProxyModel::invalidateColumnsFilter; + }; + QStandardItemModel model(10, 4); + for (int i = 0; i < model.rowCount(); ++i) { + for (int j = 0; j < model.columnCount(); ++j) { + model.setItem(i, j, new QStandardItem(QString(QChar('A' + j)) + QString::number(i + 1))); + model.item(i, 0)->appendColumn({ new QStandardItem(QString("child col %0").arg(j)) }); + } + } + FilterProxy proxy; + proxy.setSourceModel(&model); + + QTreeView view; + view.setModel(&proxy); + view.expandAll(); + + QCOMPARE(proxy.rowFiltered, 20); //10 parents + 10 children + QCOMPARE(proxy.columnFiltered, 44); // 4 parents + 4 * 10 children + + proxy.rowFiltered = proxy.columnFiltered = 0; + proxy.invalidateFilter(); + + QCOMPARE(proxy.rowFiltered, 20); + QCOMPARE(proxy.columnFiltered, 44); + + proxy.rowFiltered = proxy.columnFiltered = 0; + proxy.invalidateRowsFilter(); + + QCOMPARE(proxy.rowFiltered, 20); + QCOMPARE(proxy.columnFiltered, 0); + + proxy.rowFiltered = proxy.columnFiltered = 0; + proxy.invalidateColumnsFilter(); + + QCOMPARE(proxy.rowFiltered, 0); + QCOMPARE(proxy.columnFiltered, 44); + + QCOMPARE(proxy.rowCount(), 10); + proxy.rejectA1 = true; + proxy.rowFiltered = proxy.columnFiltered = 0; + proxy.invalidateRowsFilter(); + QCOMPARE(proxy.rowCount(), 9); + QCOMPARE(proxy.rowFiltered, 19); // it will not check the child row of A1 + + proxy.rowFiltered = proxy.columnFiltered = 0; + proxy.setRecursiveFilteringEnabled(true); // this triggers invalidateRowsFilter() + QCOMPARE(proxy.rowCount(), 10); + QCOMPARE(proxy.rowFiltered, 20); +} + +void tst_QSortFilterProxyModel::filterKeyColumnBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.filterKeyColumn(), 0); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, int>(proxyModel, 10, 5, + "filterKeyColumn"); +} + +void tst_QSortFilterProxyModel::dynamicSortFilterBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.dynamicSortFilter(), true); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, bool>(proxyModel, false, true, + "dynamicSortFilter"); +} + +void tst_QSortFilterProxyModel::sortCaseSensitivityBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.sortCaseSensitivity(), Qt::CaseSensitive); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, Qt::CaseSensitivity>( + proxyModel, Qt::CaseInsensitive, Qt::CaseSensitive, "sortCaseSensitivity"); +} + +void tst_QSortFilterProxyModel::isSortLocaleAwareBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.isSortLocaleAware(), false); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, bool>(proxyModel, true, false, + "isSortLocaleAware"); +} + +void tst_QSortFilterProxyModel::sortRoleBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.sortRole(), Qt::DisplayRole); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, int>( + proxyModel, Qt::TextAlignmentRole, Qt::ToolTipRole, "sortRole"); +} + +void tst_QSortFilterProxyModel::filterRoleBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.filterRole(), Qt::DisplayRole); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, int>( + proxyModel, Qt::DecorationRole, Qt::CheckStateRole, "filterRole"); +} + +void tst_QSortFilterProxyModel::recursiveFilteringEnabledBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.isRecursiveFilteringEnabled(), false); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, bool>( + proxyModel, true, false, "recursiveFilteringEnabled"); +} + +void tst_QSortFilterProxyModel::autoAcceptChildRowsBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.autoAcceptChildRows(), false); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, bool>(proxyModel, true, false, + "autoAcceptChildRows"); +} + +void tst_QSortFilterProxyModel::filterCaseSensitivityBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, Qt::CaseSensitivity>( + proxyModel, Qt::CaseInsensitive, Qt::CaseSensitive, "filterCaseSensitivity"); + if (QTest::currentTestFailed()) + return; + + // Make sure that setting QRegularExpression updates filterCaseSensitivity + // and invalidates its binding. + QProperty<Qt::CaseSensitivity> setter(Qt::CaseSensitive); + proxyModel.bindableFilterCaseSensitivity().setBinding(Qt::makePropertyBinding(setter)); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + QSignalSpy spy(&proxyModel, &QSortFilterProxyModel::filterCaseSensitivityChanged); + QRegularExpression regExp("pattern", QRegularExpression::CaseInsensitiveOption); + proxyModel.setFilterRegularExpression(regExp); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + QCOMPARE(spy.size(), 1); + QVERIFY(!proxyModel.bindableFilterCaseSensitivity().hasBinding()); +} + +void tst_QSortFilterProxyModel::filterRegularExpressionBinding() +{ + QSortFilterProxyModel proxyModel; + QCOMPARE(proxyModel.filterRegularExpression(), QRegularExpression()); + const QRegularExpression initial("initial", QRegularExpression::CaseInsensitiveOption); + const QRegularExpression changed("changed"); + QTestPrivate::testReadWritePropertyBasics<QSortFilterProxyModel, QRegularExpression>( + proxyModel, initial, changed, "filterRegularExpression"); + if (QTest::currentTestFailed()) + return; + + // Make sure that setting filterCaseSensitivity updates QRegularExpression + // and invalidates its binding. + QProperty<QRegularExpression> setter(initial); + proxyModel.bindableFilterRegularExpression().setBinding(Qt::makePropertyBinding(setter)); + QCOMPARE(proxyModel.filterRegularExpression(), initial); + QVERIFY(proxyModel.bindableFilterRegularExpression().hasBinding()); + + int counter = 0; + auto handler = proxyModel.bindableFilterRegularExpression().onValueChanged( + [&counter]() { ++counter; }); + Q_UNUSED(handler); + proxyModel.setFilterCaseSensitivity(Qt::CaseSensitive); + QCOMPARE(proxyModel.filterRegularExpression(), QRegularExpression(initial.pattern())); + QCOMPARE(counter, 1); + QVERIFY(!proxyModel.bindableFilterRegularExpression().hasBinding()); + + QProperty<Qt::CaseSensitivity> csSetter(Qt::CaseInsensitive); + // Make sure that setting filter string updates QRegularExpression, but does + // not break the binding for case sensitivity. + proxyModel.setFilterRegularExpression(initial); + proxyModel.bindableFilterCaseSensitivity().setBinding(Qt::makePropertyBinding(csSetter)); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + counter = 0; + proxyModel.setFilterRegularExpression("ch(ang|opp)ed"); + // The pattern has changed, but the case sensitivity options are the same. + QCOMPARE(proxyModel.filterRegularExpression(), + QRegularExpression("ch(ang|opp)ed", QRegularExpression::CaseInsensitiveOption)); + QCOMPARE(counter, 1); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + // Make sure that setting filter wildcard updates QRegularExpression, but + // does not break the binding for case sensitivity. + proxyModel.setFilterRegularExpression(initial); + proxyModel.bindableFilterCaseSensitivity().setBinding(Qt::makePropertyBinding(csSetter)); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + counter = 0; + proxyModel.setFilterWildcard("*.jpeg"); + const QString wildcardStr = QRegularExpression::wildcardToRegularExpression( + "*.jpeg", QRegularExpression::UnanchoredWildcardConversion); + // The pattern has changed, but the case sensitivity options are the same. + QCOMPARE(proxyModel.filterRegularExpression(), + QRegularExpression(wildcardStr, QRegularExpression::CaseInsensitiveOption)); + QCOMPARE(counter, 1); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + // Make sure that setting filter fixed string updates QRegularExpression, + // but does not break the binding for case sensitivity. + proxyModel.setFilterRegularExpression(initial); + proxyModel.bindableFilterCaseSensitivity().setBinding(Qt::makePropertyBinding(csSetter)); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); + + counter = 0; + proxyModel.setFilterFixedString("test"); + // The pattern has changed, but the case sensitivity options are the same. + QCOMPARE(proxyModel.filterRegularExpression(), + QRegularExpression("test", QRegularExpression::CaseInsensitiveOption)); + QCOMPARE(counter, 1); + QVERIFY(proxyModel.bindableFilterCaseSensitivity().hasBinding()); +} + +void tst_QSortFilterProxyModel::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QStandardItemModel model(3, 1); + for (int row = 0; row < 3; ++row) + model.setData(model.index(row, 0), row, Qt::UserRole); + QSortFilterProxyModel proxy; + new QAbstractItemModelTester(&proxy, &proxy); + proxy.setSourceModel(&model); + proxy.setSortRole(Qt::UserRole); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(&proxy, &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &proxy](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(proxy.index(row, 0)); + }); + connect(&proxy, &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data(Qt::UserRole).toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data(Qt::UserRole).toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data(Qt::UserRole).toInt(), 2); + }); + model.setData(model.index(1, 0), -1, Qt::UserRole); + proxy.sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +QTEST_MAIN(tst_QSortFilterProxyModel) #include "tst_qsortfilterproxymodel.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.h index 71662bda07..088a5b552e 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_common/tst_qsortfilterproxymodel.h +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel/tst_qsortfilterproxymodel.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef TST_QSORTFILTERPROXYMODEL_H #define TST_QSORTFILTERPROXYMODEL_H @@ -55,6 +30,7 @@ private slots: void sort(); void sortHierarchy_data(); void sortHierarchy(); + void createPersistentOnLayoutAboutToBeChanged(); void insertRows_data(); void insertRows(); @@ -90,6 +66,10 @@ private slots: void changeSourceData(); void changeSourceDataKeepsStableSorting_qtbug1548(); void changeSourceDataForwardsRoles_qtbug35440(); + void changeSourceDataProxySendDataChanged_qtbug87781(); + void changeSourceDataTreeModel(); + void changeSourceDataProxyFilterSingleColumn(); + void changeSourceDataProxyFilterMultipleColumns(); void resortingDoesNotBreakTreeModels(); void dynamicFilterWithoutSort(); void sortFilterRole(); @@ -159,6 +139,20 @@ private slots: void removeIntervals_data(); void removeIntervals(); + void checkFilteredIndexes(); + void invalidateColumnsOrRowsFilter(); + + void filterKeyColumnBinding(); + void dynamicSortFilterBinding(); + void sortCaseSensitivityBinding(); + void isSortLocaleAwareBinding(); + void sortRoleBinding(); + void filterRoleBinding(); + void recursiveFilteringEnabledBinding(); + void autoAcceptChildRowsBinding(); + void filterCaseSensitivityBinding(); + void filterRegularExpressionBinding(); + protected: void buildHierarchy(const QStringList &data, QAbstractItemModel *model); void checkHierarchy(const QStringList &data, const QAbstractItemModel *model); diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/CMakeLists.txt b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/CMakeLists.txt new file mode 100644 index 0000000000..2f08bf1b26 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsortfilterproxymodel_recursive Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsfpm_recursive LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsfpm_recursive + SOURCES + tst_qsortfilterproxymodel_recursive.cpp + LIBRARIES + Qt::Gui +) diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro deleted file mode 100644 index a8b793dbc6..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/qsortfilterproxymodel_recursive.pro +++ /dev/null @@ -1,8 +0,0 @@ -CONFIG += testcase -CONFIG += parallel_test -TARGET = tst_qsortfilterproxymodel_recursive - -QT += testlib - -SOURCES += tst_qsortfilterproxymodel_recursive.cpp -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp index dbafedbb5d..c6b24a489a 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_recursive/tst_qsortfilterproxymodel_recursive.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, authors Filipe Azevedo <filipe.azevedo@kdab.com> and David Faure <david.faure@kdab.com> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, authors Filipe Azevedo <filipe.azevedo@kdab.com> and David Faure <david.faure@kdab.com> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -142,9 +117,9 @@ static QString treeAsString(const QAbstractItemModel &model, const QModelIndex & static void fillModel(QStandardItemModel &model, const QString &str) { QCOMPARE(str.count('['), str.count(']')); - QStandardItem *item = 0; + QStandardItem *item = nullptr; QString data; - for ( int i = 0 ; i < str.length() ; ++i ) { + for ( int i = 0 ; i < str.size() ; ++i ) { const QChar ch = str.at(i); if ((ch == '[' || ch == ']' || ch == ' ') && !data.isEmpty()) { if (data.endsWith('*')) { @@ -715,6 +690,38 @@ private Q_SLOTS: } + void testChildrenFiltering_data() + { + QTest::addColumn<QString>("sourceStr"); + QTest::addColumn<QString>("noChildrenProxyStr"); + QTest::addColumn<QString>("childrenProxyStr"); + QTest::addColumn<QString>("noParentProxyStr"); + + QTest::newRow("filter_parent") << "[1*[1.1 1.2[1.2.1]]]" << "[1*]" << "[1*[1.1 1.2[1.2.1]]]" << "[1*[1.1 1.2[1.2.1]]]"; + QTest::newRow("filter_child") << "[1[1.1 1.2*[1.2.1]]]" << "[1[1.2*]]" << "[1[1.2*[1.2.1]]]" << ""; + + } + + void testChildrenFiltering() + { + QFETCH(QString, sourceStr); + QFETCH(QString, noChildrenProxyStr); + QFETCH(QString, childrenProxyStr); + QFETCH(QString, noParentProxyStr); + + QStandardItemModel model; + fillModel(model, sourceStr); + + TestModel proxy(&model); + QCOMPARE(treeAsString(proxy), noChildrenProxyStr); + + proxy.setAutoAcceptChildRows(true); + QCOMPARE(treeAsString(proxy), childrenProxyStr); + + proxy.setRecursiveFilteringEnabled(false); + QCOMPARE(treeAsString(proxy), noParentProxyStr); + } + private: QStandardItem *itemByText(const QStandardItemModel& model, const QString &text) const { QModelIndexList list = model.match(model.index(0, 0), Qt::DisplayRole, text, 1, Qt::MatchRecursive); diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore deleted file mode 100644 index 4fdaebc09d..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -tst_qsortfilterproxymodel_regexp diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro deleted file mode 100644 index 7c510930f4..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/qsortfilterproxymodel_regexp.pro +++ /dev/null @@ -1,16 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsortfilterproxymodel_regexp - -QT += widgets testlib -mtdir = ../../../other/qabstractitemmodelutils -qsfpmdir = ../qsortfilterproxymodel_common - -INCLUDEPATH += $$PWD/$${mtdir} $$PWD/$${qsfpmdir} -SOURCES += \ - tst_qsortfilterproxymodel_regexp.cpp \ - $${qsfpmdir}/tst_qsortfilterproxymodel.cpp \ - $${mtdir}/dynamictreemodel.cpp - -HEADERS += \ - $${qsfpmdir}/tst_qsortfilterproxymodel.h \ - $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/tst_qsortfilterproxymodel_regexp.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/tst_qsortfilterproxymodel_regexp.cpp deleted file mode 100644 index 38607f1378..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regexp/tst_qsortfilterproxymodel_regexp.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> - -#include "tst_qsortfilterproxymodel.h" - -class tst_QSortFilterProxyModelRegExp : public tst_QSortFilterProxyModel -{ - Q_OBJECT -public: - tst_QSortFilterProxyModelRegExp(); -private slots: - void tst_invalid(); - void tst_caseSensitivity(); -}; - -tst_QSortFilterProxyModelRegExp::tst_QSortFilterProxyModelRegExp() : - tst_QSortFilterProxyModel() -{ - m_filterType = FilterType::RegExp; -} - -void tst_QSortFilterProxyModelRegExp::tst_invalid() -{ - const QLatin1String pattern("test"); - QSortFilterProxyModel model; - model.setFilterRegExp(pattern); - QCOMPARE(model.filterRegExp(), QRegExp(pattern)); - model.setFilterRegularExpression(pattern); - QCOMPARE(model.filterRegExp(), QRegExp()); -} - -void tst_QSortFilterProxyModelRegExp::tst_caseSensitivity() -{ - const QLatin1String pattern("test"); - QSortFilterProxyModel model; - model.setFilterCaseSensitivity(Qt::CaseInsensitive); - model.setFilterRegExp(pattern); - QCOMPARE(model.filterCaseSensitivity(), Qt::CaseInsensitive); -} - -QTEST_MAIN(tst_QSortFilterProxyModelRegExp) -#include "tst_qsortfilterproxymodel_regexp.moc" diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/CMakeLists.txt b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/CMakeLists.txt new file mode 100644 index 0000000000..d82c2118b5 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qsortfilterproxymodel_regularexpression Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qsfpm_regex LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qsfpm_regex + SOURCES + tst_qsortfilterproxymodel_regularexpression.cpp + INCLUDE_DIRECTORIES + ../../../other/qabstractitemmodelutils + LIBRARIES + Qt::TestPrivate +) diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro deleted file mode 100644 index e993d07126..0000000000 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/qsortfilterproxymodel_regularexpression.pro +++ /dev/null @@ -1,16 +0,0 @@ -CONFIG += testcase -TARGET = tst_qsortfilterproxymodel_regularexpression - -QT += widgets testlib -mtdir = ../../../other/qabstractitemmodelutils -qsfpmdir = ../qsortfilterproxymodel_common - -INCLUDEPATH += $$PWD/$${mtdir} $${qsfpmdir} -SOURCES += \ - tst_qsortfilterproxymodel_regularexpression.cpp \ - $${qsfpmdir}/tst_qsortfilterproxymodel.cpp \ - $${mtdir}/dynamictreemodel.cpp - -HEADERS += \ - $${qsfpmdir}/tst_qsortfilterproxymodel.h \ - $${mtdir}/dynamictreemodel.h diff --git a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp index 821e199bcb..729da48c5e 100644 --- a/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp +++ b/tests/auto/corelib/itemmodels/qsortfilterproxymodel_regularexpression/tst_qsortfilterproxymodel_regularexpression.cpp @@ -1,58 +1,129 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> - -#include "tst_qsortfilterproxymodel.h" - -class tst_QSortFilterProxyModelRegularExpression : public tst_QSortFilterProxyModel +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QSignalSpy> +#include <QStringListModel> +#include <QSortFilterProxyModel> + +class tst_QSortFilterProxyModelRegularExpression : public QObject { Q_OBJECT -public: - tst_QSortFilterProxyModelRegularExpression(); private slots: void tst_invalid(); + void tst_caseSensitivity(); + void tst_keepCaseSensitivity_QTBUG_92260(); + void tst_keepPatternOptions_QTBUG_92260(); + void tst_regexCaseSensitivityNotification(); }; -tst_QSortFilterProxyModelRegularExpression::tst_QSortFilterProxyModelRegularExpression() : - tst_QSortFilterProxyModel() -{ - m_filterType = FilterType::RegularExpression; -} - void tst_QSortFilterProxyModelRegularExpression::tst_invalid() { const QLatin1String pattern("test"); QSortFilterProxyModel model; model.setFilterRegularExpression(pattern); QCOMPARE(model.filterRegularExpression(), QRegularExpression(pattern)); - model.setFilterRegExp(pattern); - QCOMPARE(model.filterRegularExpression(), QRegularExpression()); +} + +void tst_QSortFilterProxyModelRegularExpression::tst_caseSensitivity() +{ + const QLatin1String pattern("test"); + QStringListModel model({ "test", "TesT" }); + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + proxyModel.setFilterRegularExpression(pattern); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + QCOMPARE(proxyModel.rowCount(), 2); + + proxyModel.setFilterCaseSensitivity(Qt::CaseSensitive); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QCOMPARE(proxyModel.rowCount(), 1); + proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + QCOMPARE(proxyModel.rowCount(), 2); +} + +/*! + This test ensures that when a string pattern is passed to setRegularEpxression, + the options are properly reseted but that the case sensitivity is kept as is. + + */ +void tst_QSortFilterProxyModelRegularExpression::tst_keepCaseSensitivity_QTBUG_92260() +{ + const QLatin1String pattern("test"); + QStringListModel model({ "test", "TesT" }); + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + QRegularExpression patternWithOptions("Dummy", + QRegularExpression::MultilineOption + | QRegularExpression::CaseInsensitiveOption); + + proxyModel.setFilterRegularExpression(patternWithOptions); + QCOMPARE(proxyModel.filterRegularExpression(), patternWithOptions); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + + proxyModel.setFilterRegularExpression(pattern); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + QCOMPARE(proxyModel.filterRegularExpression().patternOptions(), + QRegularExpression::CaseInsensitiveOption); + + patternWithOptions.setPatternOptions(QRegularExpression::MultilineOption); + proxyModel.setFilterRegularExpression(patternWithOptions); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QCOMPARE(proxyModel.filterRegularExpression(), patternWithOptions); + + proxyModel.setFilterRegularExpression(pattern); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QCOMPARE(proxyModel.filterRegularExpression().patternOptions(), + QRegularExpression::NoPatternOption); +} + +/*! + This test ensures that when the case sensitivity is changed, it does not nuke + the pattern options that were set before. + */ +void tst_QSortFilterProxyModelRegularExpression::tst_keepPatternOptions_QTBUG_92260() +{ + QStringListModel model({ "test", "TesT" }); + QSortFilterProxyModel proxyModel; + proxyModel.setSourceModel(&model); + + QRegularExpression patternWithOptions("Dummy", + QRegularExpression::MultilineOption + | QRegularExpression::CaseInsensitiveOption); + + proxyModel.setFilterRegularExpression(patternWithOptions); + QCOMPARE(proxyModel.filterRegularExpression(), patternWithOptions); + + proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseInsensitive); + QCOMPARE(proxyModel.filterRegularExpression().patternOptions(), + patternWithOptions.patternOptions()); + + proxyModel.setFilterCaseSensitivity(Qt::CaseSensitive); + QCOMPARE(proxyModel.filterCaseSensitivity(), Qt::CaseSensitive); + QCOMPARE(proxyModel.filterRegularExpression().patternOptions(), + QRegularExpression::MultilineOption); +} + +/*! + This test ensures that if the case sensitivity is changed during a call to + setFilterRegularExpression, the notification signal will be emitted +*/ +void tst_QSortFilterProxyModelRegularExpression::tst_regexCaseSensitivityNotification() +{ + QSortFilterProxyModel proxy; + QSignalSpy spy(&proxy, &QSortFilterProxyModel::filterCaseSensitivityChanged); + proxy.setFilterCaseSensitivity(Qt::CaseInsensitive); + QCOMPARE(spy.size(), 1); + QRegularExpression re("regex"); + QVERIFY(!re.patternOptions().testFlag(QRegularExpression::CaseInsensitiveOption)); + proxy.setFilterRegularExpression(re); + QCOMPARE(proxy.filterCaseSensitivity(), Qt::CaseSensitive); + QCOMPARE(spy.size(), 2); } QTEST_MAIN(tst_QSortFilterProxyModelRegularExpression) diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qstringlistmodel/CMakeLists.txt new file mode 100644 index 0000000000..8a00d1cd42 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qstringlistmodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qstringlistmodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qstringlistmodel + SOURCES + qmodellistener.h + tst_qstringlistmodel.cpp +) diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h b/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h index e08a0bdb1f..ecf7214ba4 100644 --- a/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/qmodellistener.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QObject> #include <QModelIndex> diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro b/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro deleted file mode 100644 index 91a4cf306c..0000000000 --- a/tests/auto/corelib/itemmodels/qstringlistmodel/qstringlistmodel.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qstringlistmodel -QT = core testlib -HEADERS += qmodellistener.h -SOURCES += tst_qstringlistmodel.cpp diff --git a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp index 7182553f27..8ec2aeabe5 100644 --- a/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp +++ b/tests/auto/corelib/itemmodels/qstringlistmodel/tst_qstringlistmodel.cpp @@ -1,32 +1,8 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtTest/QtTest> +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QTest> +#include <QSignalSpy> #include <qabstractitemmodel.h> #include <qcoreapplication.h> #include <qmap.h> @@ -92,6 +68,7 @@ private slots: void itemData(); void setItemData(); + void createPersistentOnLayoutAboutToBeChanged(); }; void tst_QStringListModel::moveRowsInvalid_data() @@ -330,7 +307,7 @@ template <class C> C sorted(C c) { std::sort(c.begin(), c.end()); - return std::move(c); + return c; } void tst_QStringListModel::setData_emits_both_roles() @@ -340,7 +317,7 @@ void tst_QStringListModel::setData_emits_both_roles() QFETCH(int, role); QStringListModel model(QStringList() << "one" << "two"); - QVector<int> expected; + QList<int> expected; expected.reserve(2); expected.append(Qt::DisplayRole); expected.append(Qt::EditRole); @@ -349,7 +326,7 @@ void tst_QStringListModel::setData_emits_both_roles() QVERIFY(spy.isValid()); model.setData(model.index(row, 0), data, role); QCOMPARE(spy.size(), 1); - QCOMPARE(sorted(spy.at(0).at(2).value<QVector<int> >()), + QCOMPARE(sorted(spy.at(0).at(2).value<QList<int> >()), expected); } @@ -380,7 +357,7 @@ void tst_QStringListModel::setItemData() }}; QSignalSpy dataChangedSpy(&testModel, &QAbstractItemModel::dataChanged); QModelIndex changeIndex = testModel.index(1, 0); - const QVector<int> changeRoles{Qt::DisplayRole, Qt::EditRole}; + const QList<int> changeRoles{Qt::DisplayRole, Qt::EditRole}; const QString changedString("Changed"); QMap<int, QVariant> newItemData{std::make_pair<int>(Qt::DisplayRole, changedString)}; // invalid index does nothing and returns false @@ -393,7 +370,7 @@ void tst_QStringListModel::setItemData() QVariantList dataChangedArguments = dataChangedSpy.takeFirst(); QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex); QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex); - QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles); + QCOMPARE(dataChangedArguments.at(2).value<QList<int> >(), changeRoles); // Unsupported roles do nothing return false newItemData.clear(); newItemData.insert(Qt::UserRole, changedString); @@ -418,7 +395,7 @@ void tst_QStringListModel::setItemData() dataChangedArguments = dataChangedSpy.takeFirst(); QCOMPARE(dataChangedArguments.at(0).value<QModelIndex>(), changeIndex); QCOMPARE(dataChangedArguments.at(1).value<QModelIndex>(), changeIndex); - QCOMPARE(dataChangedArguments.at(2).value<QVector<int> >(), changeRoles); + QCOMPARE(dataChangedArguments.at(2).value<QList<int> >(), changeRoles); } void tst_QStringListModel::setData_emits_on_change_only() @@ -429,12 +406,12 @@ void tst_QStringListModel::setData_emits_on_change_only() const QModelIndex modelIdx = model.index(0, 0); const QString newStringData = QStringLiteral("test"); QVERIFY(model.setData(modelIdx, newStringData)); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); const QList<QVariant> spyList = dataChangedSpy.takeFirst(); QCOMPARE(spyList.at(0).value<QModelIndex>(), modelIdx); QCOMPARE(spyList.at(1).value<QModelIndex>(), modelIdx); - const QVector<int> expectedRoles{Qt::DisplayRole, Qt::EditRole}; - QCOMPARE(spyList.at(2).value<QVector<int> >(), expectedRoles); + const QList<int> expectedRoles{Qt::DisplayRole, Qt::EditRole}; + QCOMPARE(spyList.at(2).value<QList<int> >(), expectedRoles); QVERIFY(model.setData(modelIdx, newStringData)); QVERIFY(dataChangedSpy.isEmpty()); } @@ -446,5 +423,34 @@ void tst_QStringListModel::supportedDragDropActions() QCOMPARE(model.supportedDropActions(), Qt::CopyAction | Qt::MoveAction); } +void tst_QStringListModel::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QStringListModel model(QStringList{QStringLiteral("1"), QStringLiteral("2"), QStringLiteral("3")}); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(&model, &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(&model, &QAbstractItemModel::layoutChanged); + connect(&model, &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &model](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(model.index(row, 0)); + }); + connect(&model, &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toString(), QStringLiteral("1")); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toString(), QStringLiteral("0")); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toString(), QStringLiteral("3")); + }); + model.setData(model.index(1, 0), QStringLiteral("0")); + model.sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_MAIN(tst_QStringListModel) #include "tst_qstringlistmodel.moc" diff --git a/tests/auto/corelib/itemmodels/qtransposeproxymodel/CMakeLists.txt b/tests/auto/corelib/itemmodels/qtransposeproxymodel/CMakeLists.txt new file mode 100644 index 0000000000..b8ad00ca00 --- /dev/null +++ b/tests/auto/corelib/itemmodels/qtransposeproxymodel/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_qtransposeproxymodel Test: +##################################################################### + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtransposeproxymodel LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtransposeproxymodel + SOURCES + tst_qtransposeproxymodel.cpp + LIBRARIES + Qt::Gui +) diff --git a/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro b/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro deleted file mode 100644 index 3834add115..0000000000 --- a/tests/auto/corelib/itemmodels/qtransposeproxymodel/qtransposeproxymodel.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtransposeproxymodel -QT = core gui testlib - -SOURCES = tst_qtransposeproxymodel.cpp - diff --git a/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp b/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp index a30ac46571..127b5c6ba0 100644 --- a/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp +++ b/tests/auto/corelib/itemmodels/qtransposeproxymodel/tst_qtransposeproxymodel.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 Luca Beldi <v.ronin@yahoo.it> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QSignalSpy> @@ -84,6 +48,8 @@ private Q_SLOTS: void setItemData(); void moveRowsBase(); void moveColumnsProxy(); + void sortPersistentIndex(); + void createPersistentOnLayoutAboutToBeChanged(); private: void testTransposed( const QAbstractItemModel *const baseModel, @@ -298,8 +264,8 @@ void tst_QTransposeProxyModel::sort() QVERIFY(layoutAboutToBeChangedSpy.isValid()); QPersistentModelIndex firstIndexBeforeSort = proxyModel.index(0, 0); baseModel.sort(0, Qt::AscendingOrder); - QCOMPARE(layoutChangedSpy.count(), 1); - QCOMPARE(layoutAboutToBeChangedSpy.count(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); QCOMPARE(layoutChangedSpy.takeFirst().at(1).toInt(), int(QAbstractItemModel::HorizontalSortHint)); QCOMPARE(firstIndexBeforeSort.data().toString(), firstItemBeforeSort); for (int i = 0; i < 100; ++i) @@ -332,8 +298,8 @@ void tst_QTransposeProxyModel::removeColumnBase() QVERIFY(model->removeColumn(1, parent)); QCOMPARE(proxy.rowCount(proxy.mapFromSource(parent)), oldRowCount - 1); QCOMPARE(proxy.index(1, 0, proxy.mapFromSource(parent)).data(), expectedNewVal); - QCOMPARE(rowRemoveSpy.count(), 1); - QCOMPARE(rowAboutToBeRemoveSpy.count(), 1); + QCOMPARE(rowRemoveSpy.size(), 1); + QCOMPARE(rowAboutToBeRemoveSpy.size(), 1); for (const auto &spyArgs : {rowRemoveSpy.takeFirst(), rowAboutToBeRemoveSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent)); @@ -343,6 +309,84 @@ void tst_QTransposeProxyModel::removeColumnBase() delete model; } +void tst_QTransposeProxyModel::sortPersistentIndex() +{ + QStringListModel model(QStringList{QStringLiteral("Alice"), QStringLiteral("Charlie"), QStringLiteral("Bob")}); + QTransposeProxyModel proxy; + new QAbstractItemModelTester(&proxy, &proxy); + proxy.setSourceModel(&model); + QPersistentModelIndex aliceIdx = proxy.index(0, 0); + QPersistentModelIndex bobIdx = proxy.index(0, 2); + QPersistentModelIndex charlieIdx = proxy.index(0, 1); + connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, this, [&aliceIdx, &bobIdx, &charlieIdx](){ + QCOMPARE(aliceIdx.row(), 0); + QCOMPARE(aliceIdx.column(), 0); + QCOMPARE(aliceIdx.data().toString(), QStringLiteral("Alice")); + QCOMPARE(bobIdx.row(), 0); + QCOMPARE(bobIdx.column(), 2); + QCOMPARE(bobIdx.data().toString(), QStringLiteral("Bob")); + QCOMPARE(charlieIdx.row(), 0); + QCOMPARE(charlieIdx.column(), 1); + QCOMPARE(charlieIdx.data().toString(), QStringLiteral("Charlie")); + }); + connect(&proxy, &QAbstractItemModel::layoutChanged, this, [&aliceIdx, &bobIdx, &charlieIdx](){ + QCOMPARE(aliceIdx.row(), 0); + QCOMPARE(aliceIdx.column(), 0); + QCOMPARE(aliceIdx.data().toString(), QStringLiteral("Alice")); + QCOMPARE(bobIdx.row(), 0); + QCOMPARE(bobIdx.column(), 1); + QCOMPARE(bobIdx.data().toString(), QStringLiteral("Bob")); + QCOMPARE(charlieIdx.row(), 0); + QCOMPARE(charlieIdx.column(), 2); + QCOMPARE(charlieIdx.data().toString(), QStringLiteral("Charlie")); + }); + model.sort(0); + QCOMPARE(aliceIdx.row(), 0); + QCOMPARE(aliceIdx.column(), 0); + QCOMPARE(aliceIdx.data().toString(), QStringLiteral("Alice")); + QCOMPARE(bobIdx.row(), 0); + QCOMPARE(bobIdx.column(), 1); + QCOMPARE(bobIdx.data().toString(), QStringLiteral("Bob")); + QCOMPARE(charlieIdx.row(), 0); + QCOMPARE(charlieIdx.column(), 2); + QCOMPARE(charlieIdx.data().toString(), QStringLiteral("Charlie")); +} + +void tst_QTransposeProxyModel::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QStandardItemModel model(3, 1); + for (int row = 0; row < 3; ++row) + model.setData(model.index(row, 0), row, Qt::UserRole); + model.setSortRole(Qt::UserRole); + QTransposeProxyModel proxy; + new QAbstractItemModelTester(&proxy, &proxy); + proxy.setSourceModel(&model); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(&proxy, &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(&proxy, &QAbstractItemModel::layoutChanged); + connect(&proxy, &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &proxy](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(proxy.index(0, row)); + }); + connect(&proxy, &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 0); + QCOMPARE(idxList.at(0).column(), 1); + QCOMPARE(idxList.at(0).data(Qt::UserRole).toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data(Qt::UserRole).toInt(), -1); + QCOMPARE(idxList.at(2).row(), 0); + QCOMPARE(idxList.at(2).column(), 2); + QCOMPARE(idxList.at(2).data(Qt::UserRole).toInt(), 2); + }); + model.setData(model.index(1, 0), -1, Qt::UserRole); + model.sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + void tst_QTransposeProxyModel::insertColumnBase_data() { QTest::addColumn<QAbstractItemModel *>("model"); @@ -368,8 +412,8 @@ void tst_QTransposeProxyModel::insertColumnBase() QVERIFY(model->insertColumn(1, parent)); QCOMPARE(proxy.rowCount(proxy.mapFromSource(parent)), oldRowCount + 1); QVERIFY(!proxy.index(1, 0, proxy.mapFromSource(parent)).data().isValid()); - QCOMPARE(rowInsertSpy.count(), 1); - QCOMPARE(rowAboutToBeInsertSpy.count(), 1); + QCOMPARE(rowInsertSpy.size(), 1); + QCOMPARE(rowAboutToBeInsertSpy.size(), 1); for (const auto &spyArgs : {rowInsertSpy.takeFirst(), rowAboutToBeInsertSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent)); @@ -406,8 +450,8 @@ void tst_QTransposeProxyModel::removeRowBase() QVERIFY(model->removeRow(1, parent)); QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount - 1); QCOMPARE(proxy.index(0, 1, proxy.mapFromSource(parent)).data(), expectedNewVal); - QCOMPARE(columnsRemoveSpy.count(), 1); - QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1); + QCOMPARE(columnsRemoveSpy.size(), 1); + QCOMPARE(columnsAboutToBeRemoveSpy.size(), 1); for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(), columnsAboutToBeRemoveSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent)); @@ -442,9 +486,10 @@ void tst_QTransposeProxyModel::insertRowBase() const int oldColCount = proxy.columnCount(proxy.mapFromSource(parent)); QVERIFY(model->insertRow(1, parent)); QCOMPARE(proxy.columnCount(proxy.mapFromSource(parent)), oldColCount + 1); - QVERIFY(proxy.index(0, 1, proxy.mapFromSource(parent)).data().isNull()); - QCOMPARE(columnsInsertSpy.count(), 1); - QCOMPARE(columnsAboutToBeInsertSpy.count(), 1); + QVariant result = proxy.index(0, 1, proxy.mapFromSource(parent)).data(); + QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull())); + QCOMPARE(columnsInsertSpy.size(), 1); + QCOMPARE(columnsAboutToBeInsertSpy.size(), 1); for (const auto &spyArgs : {columnsInsertSpy.takeFirst(), columnsAboutToBeInsertSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxy.mapFromSource(parent)); @@ -489,10 +534,10 @@ void tst_QTransposeProxyModel::removeColumnProxy() QCOMPARE(model->rowCount(sourceParent), oldRowCount - 1); QCOMPARE(proxy.index(0, 1, proxyParent).data(), expectedNewVal); QCOMPARE(model->index(1, 0, sourceParent).data(), expectedNewVal); - QCOMPARE(columnsRemoveSpy.count(), 1); - QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1); - QCOMPARE(rowsRemoveSpy.count(), 1); - QCOMPARE(rowsAboutToBeRemoveSpy.count(), 1); + QCOMPARE(columnsRemoveSpy.size(), 1); + QCOMPARE(columnsAboutToBeRemoveSpy.size(), 1); + QCOMPARE(rowsRemoveSpy.size(), 1); + QCOMPARE(rowsAboutToBeRemoveSpy.size(), 1); for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(), columnsAboutToBeRemoveSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent); @@ -540,12 +585,14 @@ void tst_QTransposeProxyModel::insertColumnProxy() QVERIFY(proxy.insertColumn(1, proxyParent)); QCOMPARE(proxy.columnCount(proxyParent), oldColCount + 1); QCOMPARE(model->rowCount(sourceParent), oldRowCount + 1); - QVERIFY(proxy.index(0, 1, proxyParent).data().isNull()); - QVERIFY(model->index(1, 0, sourceParent).data().isNull()); - QCOMPARE(columnsInsertSpy.count(), 1); - QCOMPARE(columnsAboutToBeInsertSpy.count(), 1); - QCOMPARE(rowsInsertSpy.count(), 1); - QCOMPARE(rowsAboutToBeInsertSpy.count(), 1); + QVariant result = proxy.index(0, 1, proxyParent).data(); + QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull())); + result = model->index(1, 0, sourceParent).data(); + QVERIFY(result.isNull() || (result.metaType().id() == QMetaType::QString && result.toString().isNull())); + QCOMPARE(columnsInsertSpy.size(), 1); + QCOMPARE(columnsAboutToBeInsertSpy.size(), 1); + QCOMPARE(rowsInsertSpy.size(), 1); + QCOMPARE(rowsAboutToBeInsertSpy.size(), 1); for (const auto &spyArgs : {columnsInsertSpy.takeFirst(), columnsAboutToBeInsertSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), proxyParent); @@ -595,10 +642,10 @@ void tst_QTransposeProxyModel::removeRowProxy() QCOMPARE(model->columnCount(sourceParent), oldColCount - 1); QCOMPARE(proxy.index(1, 0, proxyParent).data(), expectedNewVal); QCOMPARE(model->index(0, 1, sourceParent).data(), expectedNewVal); - QCOMPARE(columnsRemoveSpy.count(), 1); - QCOMPARE(columnsAboutToBeRemoveSpy.count(), 1); - QCOMPARE(rowsRemoveSpy.count(), 1); - QCOMPARE(rowsAboutToBeRemoveSpy.count(), 1); + QCOMPARE(columnsRemoveSpy.size(), 1); + QCOMPARE(columnsAboutToBeRemoveSpy.size(), 1); + QCOMPARE(rowsRemoveSpy.size(), 1); + QCOMPARE(rowsAboutToBeRemoveSpy.size(), 1); for (const auto &spyArgs : {columnsRemoveSpy.takeFirst(), columnsAboutToBeRemoveSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent); @@ -647,10 +694,10 @@ void tst_QTransposeProxyModel::insertRowProxy() QCOMPARE(model->columnCount(sourceParent), oldColCount + 1); QVERIFY(proxy.index(1, 0, proxyParent).data().isNull()); QVERIFY(model->index(0, 1, sourceParent).data().isNull()); - QCOMPARE(columnsInsertSpy.count(), 1); - QCOMPARE(columnsAboutToBeInsertSpy.count(), 1); - QCOMPARE(rowsInsertSpy.count(), 1); - QCOMPARE(rowsAboutToBeInsertSpy.count(), 1); + QCOMPARE(columnsInsertSpy.size(), 1); + QCOMPARE(columnsAboutToBeInsertSpy.size(), 1); + QCOMPARE(rowsInsertSpy.size(), 1); + QCOMPARE(rowsAboutToBeInsertSpy.size(), 1); for (const auto &spyArgs : {columnsInsertSpy.takeFirst(), columnsAboutToBeInsertSpy.takeFirst()}) { QCOMPARE(spyArgs.at(0).value<QModelIndex>(), sourceParent); @@ -713,7 +760,7 @@ void tst_QTransposeProxyModel::span() {} QSize span(const QModelIndex &index) const override { - Q_UNUSED(index) + Q_UNUSED(index); return QSize(2, 1); } }; @@ -769,15 +816,15 @@ void tst_QTransposeProxyModel::setItemData() auto signalData = proxyDataChangeSpy.takeFirst(); QCOMPARE(signalData.at(0).value<QModelIndex>(), idx); QCOMPARE(signalData.at(1).value<QModelIndex>(), idx); - const QVector<int> expectedRoles{Qt::DisplayRole, Qt::UserRole, Qt::EditRole, Qt::UserRole + 1}; - QVector<int> receivedRoles = signalData.at(2).value<QVector<int> >(); + const QList<int> expectedRoles{Qt::DisplayRole, Qt::UserRole, Qt::EditRole, Qt::UserRole + 1}; + QList<int> receivedRoles = signalData.at(2).value<QList<int> >(); QCOMPARE(receivedRoles.size(), expectedRoles.size()); for (int role : expectedRoles) QVERIFY(receivedRoles.contains(role)); signalData = sourceDataChangeSpy.takeFirst(); QCOMPARE(signalData.at(0).value<QModelIndex>(), proxy.mapToSource(idx)); QCOMPARE(signalData.at(1).value<QModelIndex>(), proxy.mapToSource(idx)); - receivedRoles = signalData.at(2).value<QVector<int> >(); + receivedRoles = signalData.at(2).value<QList<int> >(); QCOMPARE(receivedRoles.size(), expectedRoles.size()); for (int role : expectedRoles) QVERIFY(receivedRoles.contains(role)); @@ -793,14 +840,14 @@ void tst_QTransposeProxyModel::setItemData() signalData = proxyDataChangeSpy.takeFirst(); QCOMPARE(signalData.at(0).value<QModelIndex>(), idx); QCOMPARE(signalData.at(1).value<QModelIndex>(), idx); - receivedRoles = signalData.at(2).value<QVector<int> >(); + receivedRoles = signalData.at(2).value<QList<int> >(); QCOMPARE(receivedRoles.size(), expectedRoles.size()); for (int role : expectedRoles) QVERIFY(receivedRoles.contains(role)); signalData = sourceDataChangeSpy.takeFirst(); QCOMPARE(signalData.at(0).value<QModelIndex>(), proxy.mapToSource(idx)); QCOMPARE(signalData.at(1).value<QModelIndex>(), proxy.mapToSource(idx)); - receivedRoles = signalData.at(2).value<QVector<int> >(); + receivedRoles = signalData.at(2).value<QList<int> >(); QCOMPARE(receivedRoles.size(), expectedRoles.size()); for (int role : expectedRoles) QVERIFY(receivedRoles.contains(role)); @@ -822,8 +869,8 @@ void tst_QTransposeProxyModel::moveRowsBase() QVERIFY(model.moveRows(QModelIndex(), 0, 1, QModelIndex(), 2)); for (int i = 0; i < expectedNewVal.size(); ++i) QCOMPARE(proxy.index(0, i).data(), expectedNewVal.at(i)); - QCOMPARE(columnsMoveSpy.count(), 1); - QCOMPARE(columnsAboutToBeMoveSpy.count(), 1); + QCOMPARE(columnsMoveSpy.size(), 1); + QCOMPARE(columnsAboutToBeMoveSpy.size(), 1); for (const auto &spyArgs : {columnsMoveSpy.takeFirst(), columnsAboutToBeMoveSpy.takeFirst()}) { QVERIFY(!spyArgs.at(0).value<QModelIndex>().isValid()); @@ -854,10 +901,10 @@ void tst_QTransposeProxyModel::moveColumnsProxy() QCOMPARE(proxy.index(0, i).data(), expectedNewVal.at(i)); for (int i = 0; i < expectedNewVal.size(); ++i) QCOMPARE(model.index(i, 0).data(), expectedNewVal.at(i)); - QCOMPARE(columnsMoveSpy.count(), 1); - QCOMPARE(columnsAboutToBeMoveSpy.count(), 1); - QCOMPARE(rowsMoveSpy.count(), 1); - QCOMPARE(rowsAboutToBeMoveSpy.count(), 1); + QCOMPARE(columnsMoveSpy.size(), 1); + QCOMPARE(columnsAboutToBeMoveSpy.size(), 1); + QCOMPARE(rowsMoveSpy.size(), 1); + QCOMPARE(rowsAboutToBeMoveSpy.size(), 1); for (const auto &spyArgs : {columnsMoveSpy.takeFirst(), columnsAboutToBeMoveSpy.takeFirst(), rowsMoveSpy.takeFirst(),rowsAboutToBeMoveSpy.takeFirst()}) { |