File indexing completed on 2024-12-01 03:41:34

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
0003     SPDX-FileContributor: David Faure <david.faure@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QAbstractItemModelTester>
0009 #include <QDebug>
0010 #include <QSignalSpy>
0011 #include <QStandardItemModel>
0012 #include <QTest>
0013 
0014 #include "test_model_helpers.h"
0015 #include <krearrangecolumnsproxymodel.h>
0016 using namespace TestModelHelpers;
0017 
0018 Q_DECLARE_METATYPE(QModelIndex)
0019 
0020 class tst_KRearrangeColumnsProxyModel : public QObject
0021 {
0022     Q_OBJECT
0023 
0024 private Q_SLOTS:
0025 
0026     void initTestCase()
0027     {
0028         qRegisterMetaType<QModelIndex>();
0029     }
0030 
0031     void init()
0032     {
0033         // Prepare the source model to use later on
0034         mod.clear();
0035         mod.appendRow(makeStandardItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("D")
0036                                                       << QStringLiteral("E")));
0037         mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("m") << QStringLiteral("n") << QStringLiteral("o") << QStringLiteral("p")
0038                                                                   << QStringLiteral("-")));
0039         mod.item(0, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("q") << QStringLiteral("r") << QStringLiteral("s") << QStringLiteral("t")
0040                                                                   << QStringLiteral("-")));
0041         mod.appendRow(makeStandardItems(QStringList() << QStringLiteral("E") << QStringLiteral("F") << QStringLiteral("G") << QStringLiteral("H")
0042                                                       << QStringLiteral("I")));
0043         mod.item(1, 0)->appendRow(makeStandardItems(QStringList() << QStringLiteral("x") << QStringLiteral("y") << QStringLiteral("z") << QStringLiteral(".")
0044                                                                   << QStringLiteral("-")));
0045         mod.setHorizontalHeaderLabels(QStringList() << QStringLiteral("H1") << QStringLiteral("H2") << QStringLiteral("H3") << QStringLiteral("H4")
0046                                                     << QStringLiteral("H5"));
0047 
0048         QCOMPARE(extractRowTexts(&mod, 0), QStringLiteral("ABCDE"));
0049         QCOMPARE(extractRowTexts(&mod, 0, mod.index(0, 0)), QStringLiteral("mnop-"));
0050         QCOMPARE(extractRowTexts(&mod, 1, mod.index(0, 0)), QStringLiteral("qrst-"));
0051         QCOMPARE(extractRowTexts(&mod, 1), QStringLiteral("EFGHI"));
0052         QCOMPARE(extractRowTexts(&mod, 0, mod.index(1, 0)), QStringLiteral("xyz.-"));
0053         QCOMPARE(extractHorizontalHeaderTexts(&mod), QStringLiteral("H1H2H3H4H5"));
0054 
0055         // test code to see the model
0056         // showModel(&mod);
0057     }
0058 
0059     void shouldShowNothingIfNoSourceModel()
0060     {
0061         // Given a rearrange-columns proxy with no source model
0062         KRearrangeColumnsProxyModel pm;
0063 
0064         // Then the proxy should show nothing (no columns selected)
0065         QCOMPARE(pm.rowCount(), 0);
0066         QCOMPARE(pm.columnCount(), 0);
0067         QVERIFY(!pm.index(0, 0).isValid());
0068     }
0069 
0070     void shouldShowNothingIfNoColumnSelection()
0071     {
0072         // Given a rearrange-columns proxy
0073         KRearrangeColumnsProxyModel pm;
0074 
0075         // When setting it to a source model
0076         pm.setSourceModel(&mod);
0077 
0078         // Then the proxy should show nothing (no columns selected)
0079         QCOMPARE(pm.rowCount(), mod.rowCount());
0080         QCOMPARE(pm.columnCount(), 0);
0081         QVERIFY(!pm.hasChildren());
0082     }
0083 
0084     void shouldMapColumns()
0085     {
0086         // Given a rearrange-columns proxy
0087         KRearrangeColumnsProxyModel pm;
0088         pm.setSourceColumns(QList<int>() << 3 << 1 << 0);
0089 
0090         // When using that proxy on top of an empty source model
0091         QStandardItemModel sourceModel;
0092         sourceModel.setColumnCount(4);
0093         pm.setSourceModel(&sourceModel);
0094 
0095         // Then the mapping methods should work
0096         QCOMPARE(pm.proxyColumnForSourceColumn(0), 2);
0097         QCOMPARE(pm.proxyColumnForSourceColumn(1), 1);
0098         QCOMPARE(pm.proxyColumnForSourceColumn(2), -1);
0099         QCOMPARE(pm.proxyColumnForSourceColumn(3), 0);
0100         QCOMPARE(pm.sourceColumnForProxyColumn(0), 3);
0101         QCOMPARE(pm.sourceColumnForProxyColumn(1), 1);
0102         QCOMPARE(pm.sourceColumnForProxyColumn(2), 0);
0103 
0104         // And mapFromSource should return invalid for unmapped cells
0105         QVERIFY(!pm.mapFromSource(sourceModel.index(0, 2)).isValid());
0106     }
0107 
0108     void shouldShowNothingIfNoRows()
0109     {
0110         // Given a rearrange-columns proxy
0111         KRearrangeColumnsProxyModel pm;
0112         pm.setSourceColumns(QList<int>() << 2 << 3 << 1 << 0);
0113 
0114         // When using that proxy on top of an empty source model
0115         QStandardItemModel sourceModel;
0116         sourceModel.setColumnCount(4);
0117         pm.setSourceModel(&sourceModel);
0118 
0119         // Then the proxy should show nothing
0120         QCOMPARE(pm.rowCount(), 0);
0121         QCOMPARE(pm.columnCount(), 4);
0122         QCOMPARE(pm.index(0, 0), pm.index(0, 0)); // like QAbstractItemView::setModel does in a Q_ASSERT_X
0123     }
0124 
0125     void shouldRearrangeColumns()
0126     {
0127         // Given a rearrange-columns proxy
0128         KRearrangeColumnsProxyModel pm;
0129         new QAbstractItemModelTester(&pm, &pm);
0130 
0131         // When setting it to a source model, with columns rearranged
0132         setup(pm);
0133 
0134         // Then the proxy should show columns reordered
0135         QCOMPARE(pm.rowCount(), 2);
0136 
0137         // (verify that the mapFromSource(mapToSource(x)) == x roundtrip works)
0138         for (int row = 0; row < pm.rowCount(); ++row) {
0139             for (int col = 0; col < pm.columnCount(); ++col) {
0140                 // qDebug() << "row" << row << "col" << col;
0141                 QCOMPARE(pm.mapFromSource(pm.mapToSource(pm.index(row, col))), pm.index(row, col));
0142             }
0143         }
0144         QCOMPARE(indexRowCol(pm.index(0, 0)), QStringLiteral("0,0"));
0145 
0146         QCOMPARE(pm.rowCount(pm.index(0, 0)), 2);
0147         QCOMPARE(pm.index(0, 0).parent(), QModelIndex());
0148 
0149         QCOMPARE(pm.mapToSource(pm.index(0, 0)).column(), 2); // column 0 points to C
0150         QCOMPARE(pm.mapToSource(pm.index(0, 1)).column(), 3); // column 1 points to D
0151 
0152         QCOMPARE(pm.sibling(0, 1, pm.index(0, 0)).column(), 1);
0153         QCOMPARE(pm.sibling(0, 0, pm.index(0, 1)).column(), 0);
0154 
0155         QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("CDBA"));
0156         QCOMPARE(extractRowTexts(&pm, 0, pm.index(0, 0)), QStringLiteral("opnm"));
0157         QCOMPARE(extractRowTexts(&pm, 1, pm.index(0, 0)), QStringLiteral("strq"));
0158         QCOMPARE(extractRowTexts(&pm, 1), QStringLiteral("GHFE"));
0159         QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("z.yx"));
0160         QCOMPARE(extractHorizontalHeaderTexts(&pm), QStringLiteral("H3H4H2H1"));
0161 
0162         // Verify tree structure of proxy
0163         const QModelIndex secondParent = pm.index(1, 0);
0164         QVERIFY(!secondParent.parent().isValid());
0165         QCOMPARE(indexToText(pm.index(0, 0, secondParent).parent()), indexToText(secondParent));
0166         QCOMPARE(indexToText(pm.index(0, 3, secondParent).parent()), indexToText(secondParent));
0167 
0168         QVERIFY(!pm.canFetchMore(QModelIndex()));
0169     }
0170 
0171     void shouldHandleDataChanged()
0172     {
0173         // Given a rearrange-columns proxy
0174         KRearrangeColumnsProxyModel pm;
0175         setup(pm);
0176 
0177         QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex)));
0178 
0179         // When a cell in a source model changes
0180         mod.item(0, 2)->setData("c", Qt::EditRole);
0181         mod.item(0, 3)->setData("d", Qt::EditRole);
0182 
0183         // Then the change should be notified to the proxy
0184         QCOMPARE(dataChangedSpy.count(), 2);
0185         QCOMPARE(indexToText(dataChangedSpy.at(0).at(0).toModelIndex()), indexToText(pm.index(0, 0)));
0186         QCOMPARE(indexToText(dataChangedSpy.at(1).at(0).toModelIndex()), indexToText(pm.index(0, 1)));
0187         QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("cdBA"));
0188     }
0189 
0190     void shouldHandleDataChangedInChild()
0191     {
0192         // Given a rearrange-columns proxy
0193         KRearrangeColumnsProxyModel pm;
0194         setup(pm);
0195 
0196         QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex)));
0197 
0198         // When a cell in a source model changes
0199         mod.item(1, 0)->child(0, 3)->setData(",", Qt::EditRole);
0200 
0201         // Then the change should be notified to the proxy
0202         QCOMPARE(dataChangedSpy.count(), 1);
0203         QCOMPARE(indexToText(dataChangedSpy.at(0).at(0).toModelIndex()), indexToText(pm.index(0, 1, pm.index(1, 0))));
0204         QCOMPARE(extractRowTexts(&pm, 0, pm.index(1, 0)), QStringLiteral("z,yx"));
0205     }
0206 
0207     void shouldSupportSetData()
0208     {
0209         // Given a rearrange-columns proxy
0210         KRearrangeColumnsProxyModel pm;
0211         setup(pm);
0212 
0213         QSignalSpy dataChangedSpy(&pm, SIGNAL(dataChanged(QModelIndex, QModelIndex)));
0214 
0215         // When changing data via the proxy
0216         const QModelIndex idx = pm.index(0, 2);
0217         QCOMPARE(idx.data().toString(), QStringLiteral("B"));
0218         pm.setData(idx, QStringLiteral("Z"));
0219         QCOMPARE(idx.data().toString(), QStringLiteral("Z"));
0220         QCOMPARE(extractRowTexts(&pm, 0), QStringLiteral("CDZA"));
0221         QCOMPARE(extractRowTexts(&mod, 0), QStringLiteral("AZCDE"));
0222     }
0223 
0224 private:
0225     // setup proxy
0226     void setup(KRearrangeColumnsProxyModel &pm)
0227     {
0228         pm.setSourceColumns(QList<int>() << 2 << 3 << 1 << 0);
0229         pm.setSourceModel(&mod);
0230         pm.sort(0); // don't forget this!
0231     }
0232 
0233     static QString indexRowCol(const QModelIndex &index)
0234     {
0235         if (!index.isValid()) {
0236             return QStringLiteral("invalid");
0237         }
0238         return QString::number(index.row()) + "," + QString::number(index.column());
0239     }
0240 
0241     static QString indexToText(const QModelIndex &index)
0242     {
0243         if (!index.isValid()) {
0244             return QStringLiteral("invalid");
0245         }
0246         return QString::number(index.row()) + "," + QString::number(index.column()) + ","
0247             + QString::number(reinterpret_cast<qulonglong>(index.internalPointer()), 16) + " in "
0248             + QString::number(reinterpret_cast<qulonglong>(index.model()), 16);
0249     }
0250 
0251     QStandardItemModel mod;
0252 };
0253 
0254 QTEST_MAIN(tst_KRearrangeColumnsProxyModel)
0255 
0256 #include "krearrangecolumnsproxymodeltest.moc"