File indexing completed on 2025-02-09 04:25:07
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 QTest::newRow("test-01") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"2"} << 2 << 1 << QStringList{"2"} << 0; 0166 0167 QTest::newRow("test-02") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3"} << 2; 0168 0169 QTest::newRow("test-03") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3", "9"} << 0; 0170 0171 QTest::newRow("test-04") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"9"} << 2; 0172 0173 QTest::newRow("test-05") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"9"} 0174 << 7; 0175 0176 QTest::newRow("test-06") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 0177 << QStringList{"9", "15"} << 5; 0178 0179 QTest::newRow("test-07") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 0180 << QStringList{"3", "9", "15"} << 3; 0181 0182 QTest::newRow("test-08") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 0183 << QStringList{"3", "9", "11", "15"} << 0; 0184 0185 QTest::newRow("test-09") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 0 << QStringList{"11"} 0186 << 6; 0187 0188 QTest::newRow("test-10") << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 2 0189 << QStringList{"3", "15"} << 2; 0190 0191 QTest::newRow("test-11") << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 << QStringList{"11"} << 3; 0192 0193 QTest::newRow("test-12") << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 2 << QStringList{"3", "11"} 0194 << 2; 0195 0196 QTest::newRow("test-13") << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 0197 << QStringList{"3", "9", "11"} << 1; 0198 0199 QTest::newRow("test-14") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3"} << 2 << 1 << QStringList{"3"} << 0; 0200 0201 QTest::newRow("test-15") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 0202 << QStringList{"3", "9", "11"} << 2; 0203 0204 QTest::newRow("test-16") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 0205 << QStringList{"9"} << 7; 0206 0207 QTest::newRow("test-17") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 2 0208 << QStringList{"3", "11"} << 4; 0209 0210 QTest::newRow("test-18") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"4", "6", "9", "15"} << 7 << 1 0211 << QStringList{"4"} << 5; 0212 0213 QTest::newRow("test-19") << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"6", "7"} << 1 << 0 << QStringList{"7"} << 1; 0214 } 0215 0216 void KSelectionProxyModelTest::deselection() 0217 { 0218 QFETCH(int, kspm_mode); 0219 QFETCH(QStringList, selection); 0220 QFETCH(int, expectedRowCountBefore); 0221 QFETCH(int, spyCount); 0222 QFETCH(QStringList, toDeselect); 0223 QFETCH(int, expectedRowCountAfter); 0224 0225 DynamicTreeModel tree; 0226 new ModelTest(&tree, &tree); 0227 ModelResetCommand resetCommand(&tree); 0228 resetCommand.setInitialTree( 0229 " - 1" 0230 " - - 2" 0231 " - - - 3" 0232 " - - - - 4" 0233 " - - - - - 5" 0234 " - - - - - 6" 0235 " - - - - - - 7" 0236 " - - - - 8" 0237 " - - - 9" 0238 " - - - - 10" 0239 " - - - - 11" 0240 " - - - - - 12" 0241 " - - - - - 13" 0242 " - - - - - 14" 0243 " - - 15" 0244 " - - - 16" 0245 " - - - 17"); 0246 resetCommand.doCommand(); 0247 0248 QItemSelectionModel selectionModel(&tree); 0249 0250 KSelectionProxyModel proxy(&selectionModel); 0251 new ModelTest(&proxy, &proxy); 0252 proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode)); 0253 proxy.setSourceModel(&tree); 0254 0255 QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); 0256 QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); 0257 0258 QItemSelection sel; 0259 for (auto item : selection) { 0260 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive); 0261 QCOMPARE(idxs.size(), 1); 0262 sel << QItemSelectionRange(idxs.at(0), idxs.at(0)); 0263 } 0264 selectionModel.select(sel, QItemSelectionModel::Select); 0265 0266 QCOMPARE(proxy.rowCount(), expectedRowCountBefore); 0267 0268 QItemSelection desel; 0269 for (auto item : toDeselect) { 0270 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive); 0271 QCOMPARE(idxs.size(), 1); 0272 desel << QItemSelectionRange(idxs.at(0), idxs.at(0)); 0273 } 0274 selectionModel.select(desel, QItemSelectionModel::Deselect); 0275 0276 QCOMPARE(beforeSpy.count(), spyCount); 0277 QCOMPARE(afterSpy.count(), spyCount); 0278 0279 QCOMPARE(proxy.rowCount(), expectedRowCountAfter); 0280 } 0281 0282 void KSelectionProxyModelTest::removeRows_data() 0283 { 0284 QTest::addColumn<int>("kspm_mode"); 0285 QTest::addColumn<bool>("connectSelectionModelFirst"); 0286 QTest::addColumn<bool>("emulateSingleSelectionMode"); 0287 QTest::addColumn<QStringList>("selection"); 0288 QTest::addColumn<int>("expectedRowCountBefore"); 0289 QTest::addColumn<int>("spyCount"); 0290 QTest::addColumn<QStringList>("toRemove"); 0291 QTest::addColumn<int>("expectedRowCountAfter"); 0292 0293 // Because the KSelectionProxyModel has two dependencies - a QItemSelectionModel 0294 // and a QAbstractItemModel, whichever one signals first determines the internal 0295 // code path is used to perform removal. That order is determined by order 0296 // of signal slot connections, and these tests use connectSelectionModelFirst 0297 // to test both. 0298 0299 // When using a QAbstractItemView, the SelectionMode can determine how the 0300 // selection changes when a selected row is removed. When the row is 0301 // AboutToBeRemoved, the view might change the selection to a row which is 0302 // not to be removed. This means that depending on signal-slot connection 0303 // order, the KSelectionProxyModel::sourceRowsAboutToBeRemoved method 0304 // might be executed, but then the selection can be changed before 0305 // executing the KSelectionProxyModel::sourceRowsRemoved method. These tests 0306 // are run with and without similar emulated behavior. 0307 0308 for (auto emulateSingleSelectionMode : {true, false}) { 0309 for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) { 0310 for (auto connectSelectionModelFirst : {true, false}) { 0311 const auto newRow = [&](const char *number) -> QTestData & { 0312 return QTest::newRow(QByteArray(QByteArrayLiteral("A-") + (emulateSingleSelectionMode ? "1-" : "0-") 0313 + (kspm_mode == KSelectionProxyModel::SubTreesWithoutRoots ? "SubTrees-" : "Children-") 0314 + (connectSelectionModelFirst ? "1-" : "0-") + number)); 0315 }; 0316 0317 newRow("01") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1 0318 << QStringList{"2", "2"} << (emulateSingleSelectionMode ? 2 : 0); 0319 0320 newRow("02") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 0321 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"4", "4"} << 2; 0322 0323 newRow("03") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 0324 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"5", "5"} << 2; 0325 0326 newRow("04") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 0327 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"6", "6"} << 2; 0328 0329 newRow("05") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 0330 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"7", "7"} << 2; 0331 0332 newRow("06") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1 0333 << QStringList{"1", "1"} << 0; 0334 0335 newRow("07") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1 0336 << QStringList{"9", "9"} << 1; 0337 0338 newRow("08") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 0 0339 << QStringList{"15", "15"} << 2; 0340 0341 newRow("09") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0 0342 << QStringList{"5", "5"} << (emulateSingleSelectionMode ? 1 : 0); 0343 0344 newRow("10") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0 0345 << QStringList{"4", "4"} << 0; 0346 0347 newRow("11") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0 0348 << QStringList{"3", "3"} << 0; 0349 0350 newRow("12") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0 0351 << QStringList{"2", "2"} << 0; 0352 0353 newRow("13") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"6"} << 1 << 1 0354 << QStringList{"4", "4"} << 0; 0355 } 0356 } 0357 } 0358 0359 for (auto connectSelectionModelFirst : {true, false}) { 0360 for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) { 0361 const auto newRow = [&](const char *number) -> QTestData & { 0362 return QTest::newRow(QByteArray(QByteArrayLiteral("B-") + (connectSelectionModelFirst ? "1-" : "0-") 0363 + (kspm_mode == KSelectionProxyModel::SubTreesWithoutRoots ? "SubTrees-" : "Children-") + number)); 0364 }; 0365 0366 newRow("01") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"3", "15"} << 4 << 1 << QStringList{"3", "3"} 0367 << 2; 0368 0369 newRow("02") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "15"} << 4 << 1 << QStringList{"2", "2"} 0370 << 2; 0371 0372 newRow("03") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"2", "2"} 0373 << 0; 0374 0375 newRow("04") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "3"} 0376 << 3; 0377 0378 newRow("05") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"11", "11"} 0379 << 2; 0380 0381 newRow("06") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "9"} 0382 << 0; 0383 0384 newRow("07") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11", "15"} << 7 << 1 0385 << QStringList{"3", "9"} << 2; 0386 0387 newRow("08") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1 0388 << QStringList{"3", "9"} << 0; 0389 0390 newRow("09") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1 0391 << QStringList{"3", "3"} << 3; 0392 0393 newRow("10") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1 0394 << QStringList{"9", "9"} << 2; 0395 0396 newRow("11") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"9", "9"} 0397 << 2; 0398 0399 newRow("12") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "6", "11"} 0400 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 6 : 5) << 1 << QStringList{"9", "9"} 0401 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 3 : 2); 0402 0403 newRow("13") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "11"} << 5 << 1 << QStringList{"9", "9"} 0404 << 2; 0405 0406 newRow("14") << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"6", "8", "11"} << 4 << 0 << QStringList{"8", "8"} 0407 << 4; 0408 } 0409 } 0410 0411 for (auto connectSelectionModelFirst : {true, false}) { 0412 const auto newRow = [&](const char *number) -> QTestData & { 0413 return QTest::newRow(QByteArray(QByteArrayLiteral("C-") + (connectSelectionModelFirst ? "1-" : "0-") + number)); 0414 }; 0415 0416 newRow("01") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 0417 << QStringList{"2", "2"} << 0; 0418 0419 newRow("02") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2", "3", "4"} << 3 << 1 0420 << QStringList{"2", "2"} << 0; 0421 0422 newRow("03") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1 0423 << QStringList{"2", "2"} << 0; 0424 0425 newRow("04") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1 0426 << QStringList{"4", "4"} << 1; 0427 0428 newRow("05") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "10", "11"} << 3 << 1 0429 << QStringList{"3", "9"} << 0; 0430 0431 newRow("06") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "6", "7", "10", "11"} 0432 << 5 << 1 << QStringList{"10", "11"} << 3; 0433 0434 newRow("07") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} 0435 << 5 << 1 << QStringList{"4", "8"} << 0; 0436 0437 newRow("08") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} 0438 << 5 << 1 << QStringList{"4", "4"} << 1; 0439 0440 newRow("09") << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} 0441 << 5 << 1 << QStringList{"6", "6"} << 3; 0442 } 0443 0444 for (auto connectSelectionModelFirst : {true, false}) { 0445 const auto newRow = [&](const char *number) -> QTestData & { 0446 return QTest::newRow(QByteArray(QByteArrayLiteral("D-") + (connectSelectionModelFirst ? "1-" : "0-") + number)); 0447 }; 0448 0449 newRow("01") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 0450 << QStringList{"2", "2"} << 0; 0451 0452 newRow("02") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 0453 << QStringList{"4", "4"} << 1; 0454 0455 newRow("03") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1 0456 << QStringList{"4", "4"} << 1; 0457 0458 newRow("04") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1 0459 << QStringList{"2", "2"} << 0; 0460 0461 newRow("05") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1 0462 << QStringList{"2", "2"} << 0; 0463 0464 newRow("06") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1 0465 << QStringList{"4", "4"} << 1; 0466 0467 newRow("07") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1 0468 << QStringList{"9", "9"} << 1; 0469 0470 newRow("08") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1 0471 << QStringList{"5", "6"} << 2; 0472 0473 newRow("09") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1 0474 << QStringList{"4", "8"} << 1; 0475 0476 newRow("10") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1 0477 << QStringList{"9", "9"} << 2; 0478 0479 newRow("11") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1 0480 << QStringList{"11", "11"} << 2; 0481 0482 newRow("12") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1 0483 << QStringList{"3", "3"} << 2; 0484 0485 newRow("13") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1 0486 << QStringList{"2", "2"} << 0; 0487 0488 newRow("14") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1 0489 << QStringList{"9", "9"} << 2; 0490 0491 newRow("15") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"10", "11"} << 2 << 1 0492 << QStringList{"9", "9"} << 0; 0493 0494 newRow("16") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "8"} << 2 << 1 0495 << QStringList{"3", "3"} << 0; 0496 0497 newRow("17") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1 0498 << QStringList{"11", "11"} << 0; 0499 0500 newRow("18") << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1 0501 << QStringList{"10", "11"} << 0; 0502 } 0503 } 0504 0505 void KSelectionProxyModelTest::removeRows() 0506 { 0507 QFETCH(int, kspm_mode); 0508 QFETCH(bool, connectSelectionModelFirst); 0509 QFETCH(bool, emulateSingleSelectionMode); 0510 QFETCH(QStringList, selection); 0511 QFETCH(int, expectedRowCountBefore); 0512 QFETCH(int, spyCount); 0513 QFETCH(QStringList, toRemove); 0514 QFETCH(int, expectedRowCountAfter); 0515 0516 // TODO: Plasma 6: Try again on CI if minimum required Qt version would allow it. 0517 const QString tag = QString::fromUtf8(QTest::currentDataTag()); 0518 if (tag.startsWith("A-") && tag.endsWith("-0-06")) { 0519 QSKIP("This specific test fails due to upstream bug fixed by: https://codereview.qt-project.org/c/qt/qtbase/+/487372"); 0520 } 0521 0522 DynamicTreeModel tree; 0523 new ModelTest(&tree, &tree); 0524 ModelResetCommand resetCommand(&tree); 0525 resetCommand.setInitialTree( 0526 " - 1" 0527 " - - 2" 0528 " - - - 3" 0529 " - - - - 4" 0530 " - - - - - 5" 0531 " - - - - - 6" 0532 " - - - - - - 7" 0533 " - - - - 8" 0534 " - - - 9" 0535 " - - - - 10" 0536 " - - - - 11" 0537 " - - - - - 12" 0538 " - - - - - 13" 0539 " - - - - - 14" 0540 " - - 15" 0541 " - - - 16" 0542 " - - - 17"); 0543 resetCommand.doCommand(); 0544 0545 QItemSelectionModel selectionModel; 0546 0547 if (emulateSingleSelectionMode) { 0548 QObject::connect(&tree, &QAbstractItemModel::rowsAboutToBeRemoved, &tree, [&tree, &selectionModel](QModelIndex const &p, int s, int e) { 0549 auto rmIdx = tree.index(s, 0, p); 0550 if (s == e && selectionModel.selectedIndexes().contains(rmIdx)) { 0551 auto nextIdx = tree.index(e + 1, 0, rmIdx.parent()); 0552 selectionModel.select(nextIdx, QItemSelectionModel::ClearAndSelect); 0553 } 0554 }); 0555 } 0556 0557 KSelectionProxyModel proxy; 0558 new ModelTest(&proxy, &proxy); 0559 proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode)); 0560 0561 if (connectSelectionModelFirst) { 0562 selectionModel.setModel(&tree); 0563 proxy.setSourceModel(&tree); 0564 proxy.setSelectionModel(&selectionModel); 0565 } else { 0566 proxy.setSourceModel(&tree); 0567 proxy.setSelectionModel(&selectionModel); 0568 selectionModel.setModel(&tree); 0569 } 0570 0571 QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int))); 0572 QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int))); 0573 0574 QItemSelection sel; 0575 for (auto item : selection) { 0576 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive); 0577 QCOMPARE(idxs.size(), 1); 0578 sel << QItemSelectionRange(idxs.at(0), idxs.at(0)); 0579 } 0580 selectionModel.select(sel, QItemSelectionModel::Select); 0581 0582 QCOMPARE(proxy.rowCount(), expectedRowCountBefore); 0583 0584 for (auto removePairIndex = 0; removePairIndex < toRemove.size(); removePairIndex += 2) { 0585 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex], 1, Qt::MatchRecursive); 0586 QCOMPARE(idxs.size(), 1); 0587 0588 auto startIdx = idxs.at(0); 0589 0590 idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex + 1], 1, Qt::MatchRecursive); 0591 QCOMPARE(idxs.size(), 1); 0592 0593 auto endIdx = idxs.at(0); 0594 0595 ModelRemoveCommand remove(&tree); 0596 remove.setAncestorRowNumbers(tree.indexToPath(startIdx.parent())); 0597 remove.setStartRow(startIdx.row()); 0598 remove.setEndRow(endIdx.row()); 0599 remove.doCommand(); 0600 } 0601 0602 QCOMPARE(beforeSpy.count(), spyCount); 0603 QCOMPARE(afterSpy.count(), spyCount); 0604 0605 QCOMPARE(proxy.rowCount(), expectedRowCountAfter); 0606 } 0607 0608 void KSelectionProxyModelTest::selectionMapping() 0609 { 0610 QStringListModel strings(days); 0611 QItemSelectionModel selectionModel(&strings); 0612 KSelectionProxyModel proxy(&selectionModel); 0613 proxy.setFilterBehavior(KSelectionProxyModel::SubTrees); 0614 proxy.setSourceModel(&strings); 0615 auto idx1 = strings.index(0, 0); 0616 auto idx2 = strings.index(2, 0); 0617 QItemSelection sourceSel; 0618 sourceSel << QItemSelectionRange(idx1, idx2); 0619 selectionModel.select(sourceSel, QItemSelectionModel::Select); 0620 0621 QItemSelection proxySel; 0622 proxySel << QItemSelectionRange(proxy.index(0, 0), proxy.index(2, 0)); 0623 0624 QCOMPARE(proxy.mapSelectionToSource(proxySel), sourceSel); 0625 } 0626 0627 QTEST_MAIN(KSelectionProxyModelTest) 0628 0629 #include "kselectionproxymodeltest.moc"