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

0001 /*
0002     SPDX-FileCopyrightText: 2015 Stephen Kelly <steveire@gmail.com>
0003     SPDX-FileCopyrightText: 2015 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2016 Ableton AG <info@ableton.com>
0005     SPDX-FileContributor: Stephen Kelly <stephen.kelly@ableton.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "dynamictreemodel.h"
0011 #include "modeltest.h"
0012 #include "test_model_helpers.h"
0013 
0014 #include <kselectionproxymodel.h>
0015 
0016 #include <QIdentityProxyModel>
0017 #include <QSignalSpy>
0018 #include <QStringListModel>
0019 #include <QTest>
0020 
0021 using namespace TestModelHelpers;
0022 
0023 class KSelectionProxyModelTest : public QObject
0024 {
0025     Q_OBJECT
0026 public:
0027     KSelectionProxyModelTest(QObject *parent = nullptr)
0028         : QObject(parent)
0029         , days({QStringLiteral("Monday"), QStringLiteral("Tuesday"), QStringLiteral("Wednesday"), QStringLiteral("Thursday")})
0030     {
0031     }
0032 
0033 private Q_SLOTS:
0034     void columnCountShouldBeStable();
0035     void selectOnSourceReset();
0036     void selectionMapping();
0037     void removeRows_data();
0038     void removeRows();
0039 
0040     void selectionModelModelChange();
0041     void deselection_data();
0042     void deselection();
0043 
0044 private:
0045     const QStringList days;
0046 };
0047 
0048 void KSelectionProxyModelTest::columnCountShouldBeStable()
0049 {
0050     // Given a KSelectionProxy on top of a stringlist model
0051     QStringListModel strings(days);
0052     QItemSelectionModel selectionModel(&strings);
0053     KSelectionProxyModel proxy(&selectionModel);
0054     proxy.setSourceModel(&strings);
0055 
0056     QSignalSpy rowATBISpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)));
0057     QSignalSpy rowInsertedSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
0058 
0059     // No selection => the proxy should have 0 rows, 1 column
0060     // (if it had 0 columns, it would have to emit column insertions, too much trouble)
0061     QCOMPARE(proxy.rowCount(), 0);
0062     QCOMPARE(proxy.columnCount(), 1);
0063     QCOMPARE(rowATBISpy.count(), 0);
0064     QCOMPARE(rowInsertedSpy.count(), 0);
0065 
0066     // Select second entry -> the proxy should have 1 rows, 1 column
0067     selectionModel.select(QItemSelection(strings.index(1, 0), strings.index(1, 0)), QItemSelectionModel::Select);
0068     QCOMPARE(proxy.rowCount(), 1);
0069     QCOMPARE(proxy.columnCount(), 1);
0070     QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("0,0"));
0071     QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("0,0"));
0072 }
0073 
0074 void KSelectionProxyModelTest::selectOnSourceReset()
0075 {
0076     QStringListModel strings(days);
0077     QItemSelectionModel selectionModel(&strings);
0078 
0079     connect(&strings, &QAbstractItemModel::modelReset, [&] {
0080         selectionModel.select(QItemSelection(strings.index(0, 0), strings.index(2, 0)), QItemSelectionModel::Select);
0081     });
0082 
0083     KSelectionProxyModel proxy(&selectionModel);
0084     proxy.setSourceModel(&strings);
0085 
0086     selectionModel.select(QItemSelection(strings.index(0, 0), strings.index(2, 0)), QItemSelectionModel::Select);
0087 
0088     QCOMPARE(proxy.rowCount(), 3);
0089     for (int i = 0; i < 3; ++i) {
0090         QCOMPARE(proxy.index(i, 0).data().toString(), days.at(i));
0091     }
0092 
0093     QStringList numbers = {QStringLiteral("One"), QStringLiteral("Two"), QStringLiteral("Three"), QStringLiteral("Four")};
0094     strings.setStringList(numbers);
0095 
0096     QCOMPARE(proxy.rowCount(), 3);
0097     for (int i = 0; i < 3; ++i) {
0098         QCOMPARE(proxy.index(i, 0).data().toString(), numbers.at(i));
0099     }
0100 
0101     QVERIFY(selectionModel.selection().contains(strings.index(0, 0)));
0102     QVERIFY(selectionModel.selection().contains(strings.index(1, 0)));
0103     QVERIFY(selectionModel.selection().contains(strings.index(2, 0)));
0104 }
0105 
0106 void KSelectionProxyModelTest::selectionModelModelChange()
0107 {
0108     QStringListModel strings(days);
0109     QItemSelectionModel selectionModel(&strings);
0110 
0111     QIdentityProxyModel identity;
0112     identity.setSourceModel(&strings);
0113 
0114     KSelectionProxyModel proxy(&selectionModel);
0115     proxy.setSourceModel(&identity);
0116     selectionModel.select(strings.index(0, 0), QItemSelectionModel::Select);
0117 
0118     QCOMPARE(proxy.rowCount(), 1);
0119     QCOMPARE(proxy.index(0, 0).data().toString(), QStringLiteral("Monday"));
0120 
0121     QStringListModel strings2({QStringLiteral("Today"), QStringLiteral("Tomorrow")});
0122 
0123     QSignalSpy resetSpy(&proxy, &QAbstractItemModel::modelReset);
0124 
0125     selectionModel.setModel(&strings2);
0126 
0127     QCOMPARE(resetSpy.size(), 1);
0128     QCOMPARE(proxy.rowCount(), 0);
0129 
0130     proxy.setSourceModel(&strings2);
0131     selectionModel.select(strings2.index(0, 0), QItemSelectionModel::Select);
0132 
0133     QCOMPARE(proxy.rowCount(), 1);
0134     QCOMPARE(proxy.index(0, 0).data().toString(), QStringLiteral("Today"));
0135 
0136     QSignalSpy spy(&proxy, SIGNAL(modelReset()));
0137 
0138     QStringList numbers = {QStringLiteral("One"), QStringLiteral("Two"), QStringLiteral("Three"), QStringLiteral("Four")};
0139     strings.setStringList(numbers);
0140 
0141     QCOMPARE(spy.count(), 0);
0142 
0143     strings2.setStringList(numbers);
0144 
0145     QCOMPARE(spy.count(), 1);
0146 
0147     QCOMPARE(proxy.rowCount(), 0);
0148     QVERIFY(!selectionModel.hasSelection());
0149 
0150     selectionModel.select(strings2.index(0, 0), QItemSelectionModel::Select);
0151 
0152     QCOMPARE(proxy.rowCount(), 1);
0153     QCOMPARE(proxy.index(0, 0).data().toString(), numbers.at(0));
0154 }
0155 
0156 void KSelectionProxyModelTest::deselection_data()
0157 {
0158     QTest::addColumn<int>("kspm_mode");
0159     QTest::addColumn<QStringList>("selection");
0160     QTest::addColumn<int>("expectedRowCountBefore");
0161     QTest::addColumn<int>("spyCount");
0162     QTest::addColumn<QStringList>("toDeselect");
0163     QTest::addColumn<int>("expectedRowCountAfter");
0164 
0165     auto testNumber = 1;
0166 
0167     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0168         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"2"} << 2 << 1 << QStringList{"2"} << 0;
0169     ++testNumber;
0170 
0171     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0172         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3"} << 2;
0173     ++testNumber;
0174 
0175     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0176         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3", "9"} << 0;
0177     ++testNumber;
0178 
0179     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0180         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"9"} << 2;
0181     ++testNumber;
0182 
0183     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0184         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"9"} << 7;
0185     ++testNumber;
0186 
0187     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0188         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"9", "15"} << 5;
0189     ++testNumber;
0190 
0191     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0192         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"3", "9", "15"} << 3;
0193     ++testNumber;
0194 
0195     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0196         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"3", "9", "11", "15"}
0197         << 0;
0198     ++testNumber;
0199 
0200     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0201         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 0 << QStringList{"11"} << 6;
0202     ++testNumber;
0203 
0204     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0205         << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 2 << QStringList{"3", "15"} << 2;
0206     ++testNumber;
0207 
0208     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0209         << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 << QStringList{"11"} << 3;
0210     ++testNumber;
0211 
0212     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0213         << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 2 << QStringList{"3", "11"} << 2;
0214     ++testNumber;
0215 
0216     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0217         << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 << QStringList{"3", "9", "11"} << 1;
0218     ++testNumber;
0219 
0220     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0221         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3"} << 2 << 1 << QStringList{"3"} << 0;
0222     ++testNumber;
0223 
0224     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0225         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 << QStringList{"3", "9", "11"} << 2;
0226     ++testNumber;
0227 
0228     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0229         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 << QStringList{"9"} << 7;
0230     ++testNumber;
0231 
0232     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0233         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 2 << QStringList{"3", "11"} << 4;
0234     ++testNumber;
0235 
0236     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0237         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"4", "6", "9", "15"} << 7 << 1 << QStringList{"4"} << 5;
0238     ++testNumber;
0239 
0240     QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0241         << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"6", "7"} << 1 << 0 << QStringList{"7"} << 1;
0242     ++testNumber;
0243 }
0244 
0245 void KSelectionProxyModelTest::deselection()
0246 {
0247     QFETCH(int, kspm_mode);
0248     QFETCH(QStringList, selection);
0249     QFETCH(int, expectedRowCountBefore);
0250     QFETCH(int, spyCount);
0251     QFETCH(QStringList, toDeselect);
0252     QFETCH(int, expectedRowCountAfter);
0253 
0254     DynamicTreeModel tree;
0255     new ModelTest(&tree, &tree);
0256     ModelResetCommand resetCommand(&tree);
0257     resetCommand.setInitialTree(
0258         " - 1"
0259         " - - 2"
0260         " - - - 3"
0261         " - - - - 4"
0262         " - - - - - 5"
0263         " - - - - - 6"
0264         " - - - - - - 7"
0265         " - - - - 8"
0266         " - - - 9"
0267         " - - - - 10"
0268         " - - - - 11"
0269         " - - - - - 12"
0270         " - - - - - 13"
0271         " - - - - - 14"
0272         " - - 15"
0273         " - - - 16"
0274         " - - - 17");
0275     resetCommand.doCommand();
0276 
0277     QItemSelectionModel selectionModel(&tree);
0278 
0279     KSelectionProxyModel proxy(&selectionModel);
0280     new ModelTest(&proxy, &proxy);
0281     proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode));
0282     proxy.setSourceModel(&tree);
0283 
0284     QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)));
0285     QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
0286 
0287     QItemSelection sel;
0288     for (auto item : selection) {
0289         QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
0290         QCOMPARE(idxs.size(), 1);
0291         sel << QItemSelectionRange(idxs.at(0), idxs.at(0));
0292     }
0293     selectionModel.select(sel, QItemSelectionModel::Select);
0294 
0295     QCOMPARE(proxy.rowCount(), expectedRowCountBefore);
0296 
0297     QItemSelection desel;
0298     for (auto item : toDeselect) {
0299         QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
0300         QCOMPARE(idxs.size(), 1);
0301         desel << QItemSelectionRange(idxs.at(0), idxs.at(0));
0302     }
0303     selectionModel.select(desel, QItemSelectionModel::Deselect);
0304 
0305     QCOMPARE(beforeSpy.count(), spyCount);
0306     QCOMPARE(afterSpy.count(), spyCount);
0307 
0308     QCOMPARE(proxy.rowCount(), expectedRowCountAfter);
0309 }
0310 
0311 void KSelectionProxyModelTest::removeRows_data()
0312 {
0313     QTest::addColumn<int>("kspm_mode");
0314     QTest::addColumn<bool>("connectSelectionModelFirst");
0315     QTest::addColumn<bool>("emulateSingleSelectionMode");
0316     QTest::addColumn<QStringList>("selection");
0317     QTest::addColumn<int>("expectedRowCountBefore");
0318     QTest::addColumn<int>("spyCount");
0319     QTest::addColumn<QStringList>("toRemove");
0320     QTest::addColumn<int>("expectedRowCountAfter");
0321 
0322     // Because the KSelectionProxyModel has two dependencies - a QItemSelectionModel
0323     // and a QAbstractItemModel, whichever one signals first determines the internal
0324     // code path is used to perform removal.  That order is determined by order
0325     // of signal slot connections, and these tests use connectSelectionModelFirst
0326     // to test both.
0327 
0328     // When using a QAbstractItemView, the SelectionMode can determine how the
0329     // selection changes when a selected row is removed.  When the row is
0330     // AboutToBeRemoved, the view might change the selection to a row which is
0331     // not to be removed.  This means that depending on signal-slot connection
0332     // order, the KSelectionProxyModel::sourceRowsAboutToBeRemoved method
0333     // might be executed, but then the selection can be changed before
0334     // executing the KSelectionProxyModel::sourceRowsRemoved method.  These tests
0335     // are run with and without similar emulated behavior.
0336 
0337     auto testNumber = 1;
0338 
0339     for (auto emulateSingleSelectionMode : {true, false}) {
0340         for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) {
0341             for (auto connectSelectionModelFirst : {true, false}) {
0342                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0343                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
0344                     << QStringList{"2", "2"} << (emulateSingleSelectionMode ? 2 : 0);
0345                 ++testNumber;
0346 
0347                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0348                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
0349                     << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"4", "4"} << 2;
0350                 ++testNumber;
0351 
0352                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0353                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
0354                     << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"5", "5"} << 2;
0355                 ++testNumber;
0356 
0357                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0358                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
0359                     << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"6", "6"} << 2;
0360                 ++testNumber;
0361 
0362                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0363                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
0364                     << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"7", "7"} << 2;
0365                 ++testNumber;
0366 
0367                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0368                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
0369                     << QStringList{"1", "1"} << 0;
0370                 ++testNumber;
0371 
0372                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0373                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
0374                     << QStringList{"9", "9"} << 1;
0375                 ++testNumber;
0376 
0377                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0378                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 0
0379                     << QStringList{"15", "15"} << 2;
0380                 ++testNumber;
0381 
0382                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0383                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
0384                     << QStringList{"5", "5"} << (emulateSingleSelectionMode ? 1 : 0);
0385                 ++testNumber;
0386 
0387                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0388                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
0389                     << QStringList{"4", "4"} << 0;
0390                 ++testNumber;
0391 
0392                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0393                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
0394                     << QStringList{"3", "3"} << 0;
0395                 ++testNumber;
0396 
0397                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0398                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
0399                     << QStringList{"2", "2"} << 0;
0400                 ++testNumber;
0401 
0402                 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0403                     << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"6"} << 1 << 1
0404                     << QStringList{"4", "4"} << 0;
0405                 ++testNumber;
0406             }
0407         }
0408     }
0409 
0410     for (auto connectSelectionModelFirst : {true, false}) {
0411         for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) {
0412             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0413                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"3", "15"} << 4 << 1 << QStringList{"3", "3"} << 2;
0414             ++testNumber;
0415 
0416             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0417                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "15"} << 4 << 1 << QStringList{"2", "2"} << 2;
0418             ++testNumber;
0419 
0420             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0421                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"2", "2"} << 0;
0422             ++testNumber;
0423 
0424             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0425                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "3"} << 3;
0426             ++testNumber;
0427 
0428             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0429                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"11", "11"} << 2;
0430             ++testNumber;
0431 
0432             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0433                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "9"} << 0;
0434             ++testNumber;
0435 
0436             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0437                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11", "15"} << 7 << 1 << QStringList{"3", "9"} << 2;
0438             ++testNumber;
0439 
0440             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0441                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
0442                 << QStringList{"3", "9"} << 0;
0443             ++testNumber;
0444 
0445             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0446                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
0447                 << QStringList{"3", "3"} << 3;
0448             ++testNumber;
0449 
0450             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0451                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
0452                 << QStringList{"9", "9"} << 2;
0453             ++testNumber;
0454 
0455             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0456                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"9", "9"} << 2;
0457             ++testNumber;
0458 
0459             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0460                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "6", "11"}
0461                 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 6 : 5) << 1 << QStringList{"9", "9"}
0462                 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 3 : 2);
0463             ++testNumber;
0464 
0465             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0466                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "11"} << 5 << 1 << QStringList{"9", "9"} << 2;
0467             ++testNumber;
0468 
0469             QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0470                 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"6", "8", "11"} << 4 << 0 << QStringList{"8", "8"} << 4;
0471             ++testNumber;
0472         }
0473     }
0474 
0475     for (auto connectSelectionModelFirst : {true, false}) {
0476         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0477             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1
0478             << QStringList{"2", "2"} << 0;
0479         ++testNumber;
0480 
0481         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0482             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2", "3", "4"} << 3 << 1
0483             << QStringList{"2", "2"} << 0;
0484         ++testNumber;
0485 
0486         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0487             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1
0488             << QStringList{"2", "2"} << 0;
0489         ++testNumber;
0490 
0491         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0492             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1
0493             << QStringList{"4", "4"} << 1;
0494         ++testNumber;
0495 
0496         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0497             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "10", "11"} << 3 << 1
0498             << QStringList{"3", "9"} << 0;
0499         ++testNumber;
0500 
0501         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0502             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "6", "7", "10", "11"} << 5 << 1
0503             << QStringList{"10", "11"} << 3;
0504         ++testNumber;
0505 
0506         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0507             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
0508             << QStringList{"4", "8"} << 0;
0509         ++testNumber;
0510 
0511         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0512             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
0513             << QStringList{"4", "4"} << 1;
0514 
0515         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0516             << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
0517             << QStringList{"6", "6"} << 3;
0518         ++testNumber;
0519     }
0520 
0521     for (auto connectSelectionModelFirst : {true, false}) {
0522         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0523             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 << QStringList{"2", "2"}
0524             << 0;
0525         ++testNumber;
0526 
0527         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0528             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 << QStringList{"4", "4"}
0529             << 1;
0530         ++testNumber;
0531 
0532         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0533             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1
0534             << QStringList{"4", "4"} << 1;
0535         ++testNumber;
0536 
0537         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0538             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1
0539             << QStringList{"2", "2"} << 0;
0540         ++testNumber;
0541 
0542         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0543             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
0544             << QStringList{"2", "2"} << 0;
0545         ++testNumber;
0546 
0547         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0548             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
0549             << QStringList{"4", "4"} << 1;
0550         ++testNumber;
0551 
0552         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0553             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
0554             << QStringList{"9", "9"} << 1;
0555         ++testNumber;
0556 
0557         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0558             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
0559             << QStringList{"5", "6"} << 2;
0560         ++testNumber;
0561 
0562         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0563             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
0564             << QStringList{"4", "8"} << 1;
0565         ++testNumber;
0566 
0567         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0568             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1
0569             << QStringList{"9", "9"} << 2;
0570         ++testNumber;
0571 
0572         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0573             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1
0574             << QStringList{"11", "11"} << 2;
0575         ++testNumber;
0576 
0577         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0578             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
0579             << QStringList{"3", "3"} << 2;
0580         ++testNumber;
0581 
0582         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0583             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
0584             << QStringList{"2", "2"} << 0;
0585         ++testNumber;
0586 
0587         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0588             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
0589             << QStringList{"9", "9"} << 2;
0590         ++testNumber;
0591 
0592         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0593             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"10", "11"} << 2 << 1
0594             << QStringList{"9", "9"} << 0;
0595         ++testNumber;
0596 
0597         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0598             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "8"} << 2 << 1
0599             << QStringList{"3", "3"} << 0;
0600         ++testNumber;
0601 
0602         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0603             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1
0604             << QStringList{"11", "11"} << 0;
0605         ++testNumber;
0606 
0607         QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
0608             << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1
0609             << QStringList{"10", "11"} << 0;
0610         ++testNumber;
0611     }
0612 }
0613 
0614 void KSelectionProxyModelTest::removeRows()
0615 {
0616     QFETCH(int, kspm_mode);
0617     QFETCH(bool, connectSelectionModelFirst);
0618     QFETCH(bool, emulateSingleSelectionMode);
0619     QFETCH(QStringList, selection);
0620     QFETCH(int, expectedRowCountBefore);
0621     QFETCH(int, spyCount);
0622     QFETCH(QStringList, toRemove);
0623     QFETCH(int, expectedRowCountAfter);
0624 
0625     DynamicTreeModel tree;
0626     new ModelTest(&tree, &tree);
0627     ModelResetCommand resetCommand(&tree);
0628     resetCommand.setInitialTree(
0629         " - 1"
0630         " - - 2"
0631         " - - - 3"
0632         " - - - - 4"
0633         " - - - - - 5"
0634         " - - - - - 6"
0635         " - - - - - - 7"
0636         " - - - - 8"
0637         " - - - 9"
0638         " - - - - 10"
0639         " - - - - 11"
0640         " - - - - - 12"
0641         " - - - - - 13"
0642         " - - - - - 14"
0643         " - - 15"
0644         " - - - 16"
0645         " - - - 17");
0646     resetCommand.doCommand();
0647 
0648     QItemSelectionModel selectionModel;
0649 
0650     if (emulateSingleSelectionMode) {
0651         QObject::connect(&tree, &QAbstractItemModel::rowsAboutToBeRemoved, &tree, [&tree, &selectionModel](QModelIndex const &p, int s, int e) {
0652             auto rmIdx = tree.index(s, 0, p);
0653             if (s == e && selectionModel.selectedIndexes().contains(rmIdx)) {
0654                 auto nextIdx = tree.index(e + 1, 0, rmIdx.parent());
0655                 selectionModel.select(nextIdx, QItemSelectionModel::ClearAndSelect);
0656             }
0657         });
0658     }
0659 
0660     KSelectionProxyModel proxy;
0661     new ModelTest(&proxy, &proxy);
0662     proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode));
0663 
0664     if (connectSelectionModelFirst) {
0665         selectionModel.setModel(&tree);
0666         proxy.setSourceModel(&tree);
0667         proxy.setSelectionModel(&selectionModel);
0668     } else {
0669         proxy.setSourceModel(&tree);
0670         proxy.setSelectionModel(&selectionModel);
0671         selectionModel.setModel(&tree);
0672     }
0673 
0674     QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)));
0675     QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
0676 
0677     QItemSelection sel;
0678     for (auto item : selection) {
0679         QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
0680         QCOMPARE(idxs.size(), 1);
0681         sel << QItemSelectionRange(idxs.at(0), idxs.at(0));
0682     }
0683     selectionModel.select(sel, QItemSelectionModel::Select);
0684 
0685     QCOMPARE(proxy.rowCount(), expectedRowCountBefore);
0686 
0687     for (auto removePairIndex = 0; removePairIndex < toRemove.size(); removePairIndex += 2) {
0688         QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex], 1, Qt::MatchRecursive);
0689         QCOMPARE(idxs.size(), 1);
0690 
0691         auto startIdx = idxs.at(0);
0692 
0693         idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex + 1], 1, Qt::MatchRecursive);
0694         QCOMPARE(idxs.size(), 1);
0695 
0696         auto endIdx = idxs.at(0);
0697 
0698         ModelRemoveCommand remove(&tree);
0699         remove.setAncestorRowNumbers(tree.indexToPath(startIdx.parent()));
0700         remove.setStartRow(startIdx.row());
0701         remove.setEndRow(endIdx.row());
0702         remove.doCommand();
0703     }
0704 
0705     QCOMPARE(beforeSpy.count(), spyCount);
0706     QCOMPARE(afterSpy.count(), spyCount);
0707 
0708     QCOMPARE(proxy.rowCount(), expectedRowCountAfter);
0709 }
0710 
0711 void KSelectionProxyModelTest::selectionMapping()
0712 {
0713     QStringListModel strings(days);
0714     QItemSelectionModel selectionModel(&strings);
0715     KSelectionProxyModel proxy(&selectionModel);
0716     proxy.setFilterBehavior(KSelectionProxyModel::SubTrees);
0717     proxy.setSourceModel(&strings);
0718     auto idx1 = strings.index(0, 0);
0719     auto idx2 = strings.index(2, 0);
0720     QItemSelection sourceSel;
0721     sourceSel << QItemSelectionRange(idx1, idx2);
0722     selectionModel.select(sourceSel, QItemSelectionModel::Select);
0723 
0724     QItemSelection proxySel;
0725     proxySel << QItemSelectionRange(proxy.index(0, 0), proxy.index(2, 0));
0726 
0727     QCOMPARE(proxy.mapSelectionToSource(proxySel), sourceSel);
0728 }
0729 
0730 QTEST_MAIN(KSelectionProxyModelTest)
0731 
0732 #include "kselectionproxymodeltest.moc"