File indexing completed on 2024-04-21 15:01:24

0001 /*
0002     Copyright (C) 2019 David Edmundson <davidedmundson@kde.org>
0003 
0004     This library is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Lesser General Public License as published by
0006     the Free Software Foundation; either version 2.1 of the License, or (at your
0007     option) any later version.
0008 
0009     This library is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to the
0016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301, USA.
0018 */
0019 
0020 #include <QObject>
0021 
0022 #include <QSignalSpy>
0023 #include <QTest>
0024 
0025 #include <QQmlApplicationEngine>
0026 #include <QQmlContext>
0027 
0028 #include <QAbstractItemModel>
0029 #include <QStandardItemModel>
0030 
0031 #ifdef IMPORT_ITEMMODELSPLUGIN
0032 #include <QPluginLoader>
0033 Q_IMPORT_PLUGIN(Plugin)
0034 #endif
0035 
0036 class tst_KSortFilterProxyModelQml : public QObject
0037 {
0038     Q_OBJECT
0039 private Q_SLOTS:
0040     void testFilterCallback();
0041     void testSortRole_data();
0042     void testSortRole();
0043     void testFilterRegExp();
0044     void testFilterRegExpRole();
0045 
0046 private:
0047     QAbstractItemModel *createMonthTestModel(QObject *parent);
0048 };
0049 
0050 QAbstractItemModel *tst_KSortFilterProxyModelQml::createMonthTestModel(QObject *parent)
0051 {
0052     auto testModel = new QStandardItemModel(parent);
0053     for (int i = 1; i <= 12; i++) {
0054         auto entry = new QStandardItem();
0055         entry->setData(QLocale::c().monthName(i), Qt::DisplayRole);
0056         entry->setData(i, Qt::UserRole);
0057         testModel->appendRow(entry);
0058     }
0059     testModel->setItemRoleNames({{Qt::UserRole, "user"}, {Qt::DisplayRole, "display"}});
0060     return testModel;
0061 }
0062 
0063 void tst_KSortFilterProxyModelQml::testFilterCallback()
0064 {
0065     QQmlApplicationEngine app;
0066     app.loadData(
0067         "import QtQml 2.0\n"
0068         "import org.kde.kitemmodels 1.0\n"
0069         "KSortFilterProxyModel\n"
0070         "{\n"
0071         "    property int modulo: 2\n"
0072         "    sourceModel: KNumberModel {\n"
0073         "        minimumValue: 1\n"
0074         "        maximumValue: 10\n"
0075         "    }\n"
0076         "    filterRowCallback: function(source_row, source_parent) {\n"
0077         "        return sourceModel.data(sourceModel.index(source_row, 0, source_parent), Qt.DisplayRole) % modulo == 1;\n"
0078         "    };\n"
0079         "}\n");
0080     QCOMPARE(app.rootObjects().count(), 1);
0081 
0082     auto filterModel = qobject_cast<QAbstractItemModel *>(app.rootObjects().first());
0083     QVERIFY(filterModel);
0084 
0085     QCOMPARE(filterModel->rowCount(), 5);
0086     QCOMPARE(filterModel->data(filterModel->index(0, 0)).toString(), "1");
0087     QCOMPARE(filterModel->data(filterModel->index(1, 0)).toString(), "3");
0088     QCOMPARE(filterModel->data(filterModel->index(2, 0)).toString(), "5");
0089     QCOMPARE(filterModel->data(filterModel->index(3, 0)).toString(), "7");
0090     QCOMPARE(filterModel->data(filterModel->index(4, 0)).toString(), "9");
0091 
0092     filterModel->setProperty("modulo", 3);
0093 
0094     // Nothing should change until we call invalidateFilter
0095     QCOMPARE(filterModel->rowCount(), 5);
0096     QCOMPARE(filterModel->data(filterModel->index(0, 0)).toString(), "1");
0097     QCOMPARE(filterModel->data(filterModel->index(1, 0)).toString(), "3");
0098     QCOMPARE(filterModel->data(filterModel->index(2, 0)).toString(), "5");
0099     QCOMPARE(filterModel->data(filterModel->index(3, 0)).toString(), "7");
0100     QCOMPARE(filterModel->data(filterModel->index(4, 0)).toString(), "9");
0101 
0102     // Simulate call from QML by going through metaobject rather than calling it directly
0103     const bool invalidateFilterCallOk = QMetaObject::invokeMethod(filterModel, "invalidateFilter");
0104     QVERIFY(invalidateFilterCallOk);
0105 
0106     QCOMPARE(filterModel->rowCount(), 4);
0107     QCOMPARE(filterModel->data(filterModel->index(0, 0)).toString(), "1");
0108     QCOMPARE(filterModel->data(filterModel->index(1, 0)).toString(), "4");
0109     QCOMPARE(filterModel->data(filterModel->index(2, 0)).toString(), "7");
0110     QCOMPARE(filterModel->data(filterModel->index(3, 0)).toString(), "10");
0111 }
0112 
0113 void tst_KSortFilterProxyModelQml::testSortRole_data()
0114 {
0115     // test model consists of all month names + month number as Display and UserRoler respectively
0116 
0117     QTest::addColumn<QString>("qmlContents");
0118     QTest::addColumn<QString>("result");
0119 
0120     QTest::newRow("sort by role name - display") << "KSortFilterProxyModel {"
0121                                                     " sourceModel: testModel;"
0122                                                     " sortRole: \"display\";"
0123                                                     "}"
0124                                                  << "April";
0125     QTest::newRow("sort by role name - value") << "KSortFilterProxyModel {"
0126                                                   " sourceModel: testModel;"
0127                                                   " sortRole: \"user\";"
0128                                                   "}"
0129                                                << "January";
0130     QTest::newRow("sort by role name - reset") << "KSortFilterProxyModel {"
0131                                                   " sourceModel: testModel;"
0132                                                   " sortRole: \"\";"
0133                                                   " Component.onCompleted: sortRole = \"\";"
0134                                                   "}"
0135                                                << "January";
0136 }
0137 
0138 void tst_KSortFilterProxyModelQml::testSortRole()
0139 {
0140     QQmlApplicationEngine app;
0141     QFETCH(QString, qmlContents);
0142     QFETCH(QString, result);
0143 
0144     qmlContents =
0145         "import org.kde.kitemmodels 1.0\n"
0146         "import QtQuick 2.0\n"
0147         + qmlContents;
0148 
0149     app.rootContext()->setContextProperty("testModel", createMonthTestModel(&app));
0150 
0151     app.loadData(qmlContents.toLatin1());
0152 
0153     QCOMPARE(app.rootObjects().count(), 1);
0154     auto filterModel = qobject_cast<QAbstractItemModel *>(app.rootObjects().first());
0155     QVERIFY(filterModel);
0156     QCOMPARE(filterModel->rowCount(), 12);
0157     QCOMPARE(filterModel->data(filterModel->index(0, 0), Qt::DisplayRole).toString(), result);
0158 }
0159 
0160 void tst_KSortFilterProxyModelQml::testFilterRegExp()
0161 {
0162     // filterRegExp comes from the QSortFilterProxyModel directly, confirm it still works
0163     QQmlApplicationEngine app;
0164 
0165     app.rootContext()->setContextProperty("testModel", createMonthTestModel(&app));
0166 
0167     auto qmlSrc = QByteArray(
0168         "import QtQml 2.0\n"
0169         "import org.kde.kitemmodels 1.0\n"
0170         "KSortFilterProxyModel {\n"
0171         " sourceModel: testModel\n"
0172         " filterRegularExpression: /Ma.*/\n"
0173         "}\n");
0174 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0175     qmlSrc.replace("filterRegularExpression", "filterRegExp");
0176 #endif
0177     app.loadData(qmlSrc);
0178 
0179     QCOMPARE(app.rootObjects().count(), 1);
0180     auto filterModel = qobject_cast<QAbstractItemModel *>(app.rootObjects().first());
0181     QVERIFY(filterModel);
0182     QCOMPARE(filterModel->rowCount(), 2);
0183     QCOMPARE(filterModel->data(filterModel->index(0, 0), Qt::DisplayRole).toString(), "March");
0184     QCOMPARE(filterModel->data(filterModel->index(1, 0), Qt::DisplayRole).toString(), "May");
0185 }
0186 
0187 void tst_KSortFilterProxyModelQml::testFilterRegExpRole()
0188 {
0189     // filterRegExp comes from the QSortFilterProxyModel directly, confirm it still works
0190     QQmlApplicationEngine app;
0191 
0192     app.rootContext()->setContextProperty("testModel", createMonthTestModel(&app));
0193 
0194     auto qmlSrc = QByteArray(
0195         "import QtQml 2.0\n"
0196         "import org.kde.kitemmodels 1.0\n"
0197         "KSortFilterProxyModel {\n"
0198         " sourceModel: testModel\n"
0199         " filterRole: \"user\"\n"
0200         " filterRegularExpression: /1[0-9]/\n" // month value is 10 or more
0201         "}\n");
0202 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0203     qmlSrc.replace("filterRegularExpression", "filterRegExp");
0204 #endif
0205     app.loadData(qmlSrc);
0206 
0207     QCOMPARE(app.rootObjects().count(), 1);
0208     auto filterModel = qobject_cast<QAbstractItemModel *>(app.rootObjects().first());
0209     QVERIFY(filterModel);
0210     QCOMPARE(filterModel->rowCount(), 3);
0211     QCOMPARE(filterModel->data(filterModel->index(0, 0), Qt::DisplayRole).toString(), "October");
0212     QCOMPARE(filterModel->data(filterModel->index(1, 0), Qt::DisplayRole).toString(), "November");
0213     QCOMPARE(filterModel->data(filterModel->index(2, 0), Qt::DisplayRole).toString(), "December");
0214 }
0215 
0216 QTEST_GUILESS_MAIN(tst_KSortFilterProxyModelQml)
0217 
0218 #include "ksortfilterproxymodel_qml.moc"