1 /*
2 SPDX-FileCopyrightText: 2015 Stephen Kelly <steveire@gmail.com>
3 SPDX-FileCopyrightText: 2015 David Faure <faure@kde.org>
4 SPDX-FileCopyrightText: 2016 Ableton AG <info@ableton.com>
5 SPDX-FileContributor: Stephen Kelly <stephen.kelly@ableton.com>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9
10 #include "dynamictreemodel.h"
11 #include "modeltest.h"
12 #include "test_model_helpers.h"
13
14 #include <kselectionproxymodel.h>
15
16 #include <QIdentityProxyModel>
17 #include <QSignalSpy>
18 #include <QStringListModel>
19 #include <QTest>
20
21 using namespace TestModelHelpers;
22
23 class KSelectionProxyModelTest : public QObject
24 {
25 Q_OBJECT
26 public:
KSelectionProxyModelTest(QObject * parent=nullptr)27 KSelectionProxyModelTest(QObject *parent = nullptr)
28 : QObject(parent)
29 , days({QStringLiteral("Monday"), QStringLiteral("Tuesday"), QStringLiteral("Wednesday"), QStringLiteral("Thursday")})
30 {
31 }
32
33 private Q_SLOTS:
34 void columnCountShouldBeStable();
35 void selectOnSourceReset();
36 void selectionMapping();
37 void removeRows_data();
38 void removeRows();
39
40 void selectionModelModelChange();
41 void deselection_data();
42 void deselection();
43
44 private:
45 const QStringList days;
46 };
47
columnCountShouldBeStable()48 void KSelectionProxyModelTest::columnCountShouldBeStable()
49 {
50 // Given a KSelectionProxy on top of a stringlist model
51 QStringListModel strings(days);
52 QItemSelectionModel selectionModel(&strings);
53 KSelectionProxyModel proxy(&selectionModel);
54 proxy.setSourceModel(&strings);
55
56 QSignalSpy rowATBISpy(&proxy, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)));
57 QSignalSpy rowInsertedSpy(&proxy, SIGNAL(rowsInserted(QModelIndex, int, int)));
58
59 // No selection => the proxy should have 0 rows, 1 column
60 // (if it had 0 columns, it would have to emit column insertions, too much trouble)
61 QCOMPARE(proxy.rowCount(), 0);
62 QCOMPARE(proxy.columnCount(), 1);
63 QCOMPARE(rowATBISpy.count(), 0);
64 QCOMPARE(rowInsertedSpy.count(), 0);
65
66 // Select second entry -> the proxy should have 1 rows, 1 column
67 selectionModel.select(QItemSelection(strings.index(1, 0), strings.index(1, 0)), QItemSelectionModel::Select);
68 QCOMPARE(proxy.rowCount(), 1);
69 QCOMPARE(proxy.columnCount(), 1);
70 QCOMPARE(rowSpyToText(rowATBISpy), QStringLiteral("0,0"));
71 QCOMPARE(rowSpyToText(rowInsertedSpy), QStringLiteral("0,0"));
72 }
73
selectOnSourceReset()74 void KSelectionProxyModelTest::selectOnSourceReset()
75 {
76 QStringListModel strings(days);
77 QItemSelectionModel selectionModel(&strings);
78
79 connect(&strings, &QAbstractItemModel::modelReset, [&] {
80 selectionModel.select(QItemSelection(strings.index(0, 0), strings.index(2, 0)), QItemSelectionModel::Select);
81 });
82
83 KSelectionProxyModel proxy(&selectionModel);
84 proxy.setSourceModel(&strings);
85
86 selectionModel.select(QItemSelection(strings.index(0, 0), strings.index(2, 0)), QItemSelectionModel::Select);
87
88 QCOMPARE(proxy.rowCount(), 3);
89 for (int i = 0; i < 3; ++i) {
90 QCOMPARE(proxy.index(i, 0).data().toString(), days.at(i));
91 }
92
93 QStringList numbers = {QStringLiteral("One"), QStringLiteral("Two"), QStringLiteral("Three"), QStringLiteral("Four")};
94 strings.setStringList(numbers);
95
96 QCOMPARE(proxy.rowCount(), 3);
97 for (int i = 0; i < 3; ++i) {
98 QCOMPARE(proxy.index(i, 0).data().toString(), numbers.at(i));
99 }
100
101 QVERIFY(selectionModel.selection().contains(strings.index(0, 0)));
102 QVERIFY(selectionModel.selection().contains(strings.index(1, 0)));
103 QVERIFY(selectionModel.selection().contains(strings.index(2, 0)));
104 }
105
selectionModelModelChange()106 void KSelectionProxyModelTest::selectionModelModelChange()
107 {
108 QStringListModel strings(days);
109 QItemSelectionModel selectionModel(&strings);
110
111 QIdentityProxyModel identity;
112 identity.setSourceModel(&strings);
113
114 KSelectionProxyModel proxy(&selectionModel);
115 proxy.setSourceModel(&identity);
116 selectionModel.select(strings.index(0, 0), QItemSelectionModel::Select);
117
118 QCOMPARE(proxy.rowCount(), 1);
119 QCOMPARE(proxy.index(0, 0).data().toString(), QStringLiteral("Monday"));
120
121 QStringListModel strings2({QStringLiteral("Today"), QStringLiteral("Tomorrow")});
122
123 QSignalSpy resetSpy(&proxy, &QAbstractItemModel::modelReset);
124
125 selectionModel.setModel(&strings2);
126
127 QCOMPARE(resetSpy.size(), 1);
128 QCOMPARE(proxy.rowCount(), 0);
129
130 proxy.setSourceModel(&strings2);
131 selectionModel.select(strings2.index(0, 0), QItemSelectionModel::Select);
132
133 QCOMPARE(proxy.rowCount(), 1);
134 QCOMPARE(proxy.index(0, 0).data().toString(), QStringLiteral("Today"));
135
136 QSignalSpy spy(&proxy, SIGNAL(modelReset()));
137
138 QStringList numbers = {QStringLiteral("One"), QStringLiteral("Two"), QStringLiteral("Three"), QStringLiteral("Four")};
139 strings.setStringList(numbers);
140
141 QCOMPARE(spy.count(), 0);
142
143 strings2.setStringList(numbers);
144
145 QCOMPARE(spy.count(), 1);
146
147 QCOMPARE(proxy.rowCount(), 0);
148 QVERIFY(!selectionModel.hasSelection());
149
150 selectionModel.select(strings2.index(0, 0), QItemSelectionModel::Select);
151
152 QCOMPARE(proxy.rowCount(), 1);
153 QCOMPARE(proxy.index(0, 0).data().toString(), numbers.at(0));
154 }
155
deselection_data()156 void KSelectionProxyModelTest::deselection_data()
157 {
158 QTest::addColumn<int>("kspm_mode");
159 QTest::addColumn<QStringList>("selection");
160 QTest::addColumn<int>("expectedRowCountBefore");
161 QTest::addColumn<int>("spyCount");
162 QTest::addColumn<QStringList>("toDeselect");
163 QTest::addColumn<int>("expectedRowCountAfter");
164
165 auto testNumber = 1;
166
167 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
168 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"2"} << 2 << 1 << QStringList{"2"} << 0;
169 ++testNumber;
170
171 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
172 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3"} << 2;
173 ++testNumber;
174
175 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
176 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"3", "9"} << 0;
177 ++testNumber;
178
179 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
180 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9"} << 4 << 1 << QStringList{"9"} << 2;
181 ++testNumber;
182
183 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
184 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"9"} << 7;
185 ++testNumber;
186
187 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
188 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"9", "15"} << 5;
189 ++testNumber;
190
191 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
192 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"3", "9", "15"} << 3;
193 ++testNumber;
194
195 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
196 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 1 << QStringList{"3", "9", "11", "15"}
197 << 0;
198 ++testNumber;
199
200 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
201 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 0 << QStringList{"11"} << 6;
202 ++testNumber;
203
204 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
205 << static_cast<int>(KSelectionProxyModel::SubTreesWithoutRoots) << QStringList{"3", "9", "11", "15"} << 6 << 2 << QStringList{"3", "15"} << 2;
206 ++testNumber;
207
208 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
209 << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 << QStringList{"11"} << 3;
210 ++testNumber;
211
212 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
213 << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 2 << QStringList{"3", "11"} << 2;
214 ++testNumber;
215
216 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
217 << static_cast<int>(KSelectionProxyModel::ExactSelection) << QStringList{"3", "9", "11", "15"} << 4 << 1 << QStringList{"3", "9", "11"} << 1;
218 ++testNumber;
219
220 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
221 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3"} << 2 << 1 << QStringList{"3"} << 0;
222 ++testNumber;
223
224 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
225 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 << QStringList{"3", "9", "11"} << 2;
226 ++testNumber;
227
228 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
229 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 1 << QStringList{"9"} << 7;
230 ++testNumber;
231
232 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
233 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"3", "9", "11", "15"} << 9 << 2 << QStringList{"3", "11"} << 4;
234 ++testNumber;
235
236 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
237 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"4", "6", "9", "15"} << 7 << 1 << QStringList{"4"} << 5;
238 ++testNumber;
239
240 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
241 << static_cast<int>(KSelectionProxyModel::ChildrenOfExactSelection) << QStringList{"6", "7"} << 1 << 0 << QStringList{"7"} << 1;
242 ++testNumber;
243 }
244
deselection()245 void KSelectionProxyModelTest::deselection()
246 {
247 QFETCH(int, kspm_mode);
248 QFETCH(QStringList, selection);
249 QFETCH(int, expectedRowCountBefore);
250 QFETCH(int, spyCount);
251 QFETCH(QStringList, toDeselect);
252 QFETCH(int, expectedRowCountAfter);
253
254 DynamicTreeModel tree;
255 new ModelTest(&tree, &tree);
256 ModelResetCommand resetCommand(&tree);
257 resetCommand.setInitialTree(
258 " - 1"
259 " - - 2"
260 " - - - 3"
261 " - - - - 4"
262 " - - - - - 5"
263 " - - - - - 6"
264 " - - - - - - 7"
265 " - - - - 8"
266 " - - - 9"
267 " - - - - 10"
268 " - - - - 11"
269 " - - - - - 12"
270 " - - - - - 13"
271 " - - - - - 14"
272 " - - 15"
273 " - - - 16"
274 " - - - 17");
275 resetCommand.doCommand();
276
277 QItemSelectionModel selectionModel(&tree);
278
279 KSelectionProxyModel proxy(&selectionModel);
280 new ModelTest(&proxy, &proxy);
281 proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode));
282 proxy.setSourceModel(&tree);
283
284 QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)));
285 QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
286
287 QItemSelection sel;
288 for (auto item : selection) {
289 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
290 QCOMPARE(idxs.size(), 1);
291 sel << QItemSelectionRange(idxs.at(0), idxs.at(0));
292 }
293 selectionModel.select(sel, QItemSelectionModel::Select);
294
295 QCOMPARE(proxy.rowCount(), expectedRowCountBefore);
296
297 QItemSelection desel;
298 for (auto item : toDeselect) {
299 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
300 QCOMPARE(idxs.size(), 1);
301 desel << QItemSelectionRange(idxs.at(0), idxs.at(0));
302 }
303 selectionModel.select(desel, QItemSelectionModel::Deselect);
304
305 QCOMPARE(beforeSpy.count(), spyCount);
306 QCOMPARE(afterSpy.count(), spyCount);
307
308 QCOMPARE(proxy.rowCount(), expectedRowCountAfter);
309 }
310
removeRows_data()311 void KSelectionProxyModelTest::removeRows_data()
312 {
313 QTest::addColumn<int>("kspm_mode");
314 QTest::addColumn<bool>("connectSelectionModelFirst");
315 QTest::addColumn<bool>("emulateSingleSelectionMode");
316 QTest::addColumn<QStringList>("selection");
317 QTest::addColumn<int>("expectedRowCountBefore");
318 QTest::addColumn<int>("spyCount");
319 QTest::addColumn<QStringList>("toRemove");
320 QTest::addColumn<int>("expectedRowCountAfter");
321
322 // Because the KSelectionProxyModel has two dependencies - a QItemSelectionModel
323 // and a QAbstractItemModel, whichever one signals first determines the internal
324 // code path is used to perform removal. That order is determined by order
325 // of signal slot connections, and these tests use connectSelectionModelFirst
326 // to test both.
327
328 // When using a QAbstractItemView, the SelectionMode can determine how the
329 // selection changes when a selected row is removed. When the row is
330 // AboutToBeRemoved, the view might change the selection to a row which is
331 // not to be removed. This means that depending on signal-slot connection
332 // order, the KSelectionProxyModel::sourceRowsAboutToBeRemoved method
333 // might be executed, but then the selection can be changed before
334 // executing the KSelectionProxyModel::sourceRowsRemoved method. These tests
335 // are run with and without similar emulated behavior.
336
337 auto testNumber = 1;
338
339 for (auto emulateSingleSelectionMode : {true, false}) {
340 for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) {
341 for (auto connectSelectionModelFirst : {true, false}) {
342 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
343 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
344 << QStringList{"2", "2"} << (emulateSingleSelectionMode ? 2 : 0);
345 ++testNumber;
346
347 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
348 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
349 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"4", "4"} << 2;
350 ++testNumber;
351
352 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
353 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
354 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"5", "5"} << 2;
355 ++testNumber;
356
357 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
358 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
359 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"6", "6"} << 2;
360 ++testNumber;
361
362 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
363 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2
364 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 0 : 1) << QStringList{"7", "7"} << 2;
365 ++testNumber;
366
367 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
368 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
369 << QStringList{"1", "1"} << 0;
370 ++testNumber;
371
372 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
373 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 1
374 << QStringList{"9", "9"} << 1;
375 ++testNumber;
376
377 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
378 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"2"} << 2 << 0
379 << QStringList{"15", "15"} << 2;
380 ++testNumber;
381
382 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
383 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
384 << QStringList{"5", "5"} << (emulateSingleSelectionMode ? 1 : 0);
385 ++testNumber;
386
387 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
388 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
389 << QStringList{"4", "4"} << 0;
390 ++testNumber;
391
392 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
393 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
394 << QStringList{"3", "3"} << 0;
395 ++testNumber;
396
397 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
398 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"5"} << 0 << 0
399 << QStringList{"2", "2"} << 0;
400 ++testNumber;
401
402 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
403 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << emulateSingleSelectionMode << QStringList{"6"} << 1 << 1
404 << QStringList{"4", "4"} << 0;
405 ++testNumber;
406 }
407 }
408 }
409
410 for (auto connectSelectionModelFirst : {true, false}) {
411 for (auto kspm_mode : {KSelectionProxyModel::SubTreesWithoutRoots, KSelectionProxyModel::ChildrenOfExactSelection}) {
412 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
413 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"3", "15"} << 4 << 1 << QStringList{"3", "3"} << 2;
414 ++testNumber;
415
416 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
417 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "15"} << 4 << 1 << QStringList{"2", "2"} << 2;
418 ++testNumber;
419
420 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
421 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"2", "2"} << 0;
422 ++testNumber;
423
424 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
425 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "3"} << 3;
426 ++testNumber;
427
428 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
429 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"11", "11"} << 2;
430 ++testNumber;
431
432 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
433 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"3", "9"} << 0;
434 ++testNumber;
435
436 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
437 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11", "15"} << 7 << 1 << QStringList{"3", "9"} << 2;
438 ++testNumber;
439
440 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
441 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
442 << QStringList{"3", "9"} << 0;
443 ++testNumber;
444
445 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
446 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
447 << QStringList{"3", "3"} << 3;
448 ++testNumber;
449
450 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
451 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "10", "11", "16"} << 5 << 1
452 << QStringList{"9", "9"} << 2;
453 ++testNumber;
454
455 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
456 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "11"} << 5 << 1 << QStringList{"9", "9"} << 2;
457 ++testNumber;
458
459 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
460 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "6", "11"}
461 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 6 : 5) << 1 << QStringList{"9", "9"}
462 << (kspm_mode == KSelectionProxyModel::ChildrenOfExactSelection ? 3 : 2);
463 ++testNumber;
464
465 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
466 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"4", "8", "11"} << 5 << 1 << QStringList{"9", "9"} << 2;
467 ++testNumber;
468
469 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
470 << static_cast<int>(kspm_mode) << connectSelectionModelFirst << false << QStringList{"6", "8", "11"} << 4 << 0 << QStringList{"8", "8"} << 4;
471 ++testNumber;
472 }
473 }
474
475 for (auto connectSelectionModelFirst : {true, false}) {
476 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
477 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1
478 << QStringList{"2", "2"} << 0;
479 ++testNumber;
480
481 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
482 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"2", "3", "4"} << 3 << 1
483 << QStringList{"2", "2"} << 0;
484 ++testNumber;
485
486 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
487 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1
488 << QStringList{"2", "2"} << 0;
489 ++testNumber;
490
491 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
492 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"6", "9"} << 2 << 1
493 << QStringList{"4", "4"} << 1;
494 ++testNumber;
495
496 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
497 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "10", "11"} << 3 << 1
498 << QStringList{"3", "9"} << 0;
499 ++testNumber;
500
501 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
502 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "6", "7", "10", "11"} << 5 << 1
503 << QStringList{"10", "11"} << 3;
504 ++testNumber;
505
506 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
507 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
508 << QStringList{"4", "8"} << 0;
509 ++testNumber;
510
511 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
512 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
513 << QStringList{"4", "4"} << 1;
514
515 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
516 << static_cast<int>(KSelectionProxyModel::ExactSelection) << connectSelectionModelFirst << false << QStringList{"4", "5", "6", "7", "8"} << 5 << 1
517 << QStringList{"6", "6"} << 3;
518 ++testNumber;
519 }
520
521 for (auto connectSelectionModelFirst : {true, false}) {
522 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
523 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 << QStringList{"2", "2"}
524 << 0;
525 ++testNumber;
526
527 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
528 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2"} << 1 << 1 << QStringList{"4", "4"}
529 << 1;
530 ++testNumber;
531
532 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
533 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1
534 << QStringList{"4", "4"} << 1;
535 ++testNumber;
536
537 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
538 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"2", "4"} << 1 << 1
539 << QStringList{"2", "2"} << 0;
540 ++testNumber;
541
542 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
543 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
544 << QStringList{"2", "2"} << 0;
545 ++testNumber;
546
547 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
548 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
549 << QStringList{"4", "4"} << 1;
550 ++testNumber;
551
552 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
553 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
554 << QStringList{"9", "9"} << 1;
555 ++testNumber;
556
557 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
558 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
559 << QStringList{"5", "6"} << 2;
560 ++testNumber;
561
562 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
563 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "9"} << 2 << 1
564 << QStringList{"4", "8"} << 1;
565 ++testNumber;
566
567 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
568 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1
569 << QStringList{"9", "9"} << 2;
570 ++testNumber;
571
572 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
573 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "11", "15"} << 3 << 1
574 << QStringList{"11", "11"} << 2;
575 ++testNumber;
576
577 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
578 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
579 << QStringList{"3", "3"} << 2;
580 ++testNumber;
581
582 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
583 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
584 << QStringList{"2", "2"} << 0;
585 ++testNumber;
586
587 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
588 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"6", "8", "10", "11"} << 4 << 1
589 << QStringList{"9", "9"} << 2;
590 ++testNumber;
591
592 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
593 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"10", "11"} << 2 << 1
594 << QStringList{"9", "9"} << 0;
595 ++testNumber;
596
597 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
598 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"4", "8"} << 2 << 1
599 << QStringList{"3", "3"} << 0;
600 ++testNumber;
601
602 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
603 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1
604 << QStringList{"11", "11"} << 0;
605 ++testNumber;
606
607 QTest::newRow(QByteArray("test" + QByteArray::number(testNumber)).data())
608 << static_cast<int>(KSelectionProxyModel::SubTrees) << connectSelectionModelFirst << false << QStringList{"12", "13", "14"} << 3 << 1
609 << QStringList{"10", "11"} << 0;
610 ++testNumber;
611 }
612 }
613
removeRows()614 void KSelectionProxyModelTest::removeRows()
615 {
616 QFETCH(int, kspm_mode);
617 QFETCH(bool, connectSelectionModelFirst);
618 QFETCH(bool, emulateSingleSelectionMode);
619 QFETCH(QStringList, selection);
620 QFETCH(int, expectedRowCountBefore);
621 QFETCH(int, spyCount);
622 QFETCH(QStringList, toRemove);
623 QFETCH(int, expectedRowCountAfter);
624
625 DynamicTreeModel tree;
626 new ModelTest(&tree, &tree);
627 ModelResetCommand resetCommand(&tree);
628 resetCommand.setInitialTree(
629 " - 1"
630 " - - 2"
631 " - - - 3"
632 " - - - - 4"
633 " - - - - - 5"
634 " - - - - - 6"
635 " - - - - - - 7"
636 " - - - - 8"
637 " - - - 9"
638 " - - - - 10"
639 " - - - - 11"
640 " - - - - - 12"
641 " - - - - - 13"
642 " - - - - - 14"
643 " - - 15"
644 " - - - 16"
645 " - - - 17");
646 resetCommand.doCommand();
647
648 QItemSelectionModel selectionModel;
649
650 if (emulateSingleSelectionMode) {
651 QObject::connect(&tree, &QAbstractItemModel::rowsAboutToBeRemoved, &tree, [&tree, &selectionModel](QModelIndex const &p, int s, int e) {
652 auto rmIdx = tree.index(s, 0, p);
653 if (s == e && selectionModel.selectedIndexes().contains(rmIdx)) {
654 auto nextIdx = tree.index(e + 1, 0, rmIdx.parent());
655 selectionModel.select(nextIdx, QItemSelectionModel::ClearAndSelect);
656 }
657 });
658 }
659
660 KSelectionProxyModel proxy;
661 new ModelTest(&proxy, &proxy);
662 proxy.setFilterBehavior(static_cast<KSelectionProxyModel::FilterBehavior>(kspm_mode));
663
664 if (connectSelectionModelFirst) {
665 selectionModel.setModel(&tree);
666 proxy.setSourceModel(&tree);
667 proxy.setSelectionModel(&selectionModel);
668 } else {
669 proxy.setSourceModel(&tree);
670 proxy.setSelectionModel(&selectionModel);
671 selectionModel.setModel(&tree);
672 }
673
674 QSignalSpy beforeSpy(&proxy, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)));
675 QSignalSpy afterSpy(&proxy, SIGNAL(rowsRemoved(QModelIndex, int, int)));
676
677 QItemSelection sel;
678 for (auto item : selection) {
679 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, item, 1, Qt::MatchRecursive);
680 QCOMPARE(idxs.size(), 1);
681 sel << QItemSelectionRange(idxs.at(0), idxs.at(0));
682 }
683 selectionModel.select(sel, QItemSelectionModel::Select);
684
685 QCOMPARE(proxy.rowCount(), expectedRowCountBefore);
686
687 for (auto removePairIndex = 0; removePairIndex < toRemove.size(); removePairIndex += 2) {
688 QModelIndexList idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex], 1, Qt::MatchRecursive);
689 QCOMPARE(idxs.size(), 1);
690
691 auto startIdx = idxs.at(0);
692
693 idxs = tree.match(tree.index(0, 0), Qt::DisplayRole, toRemove[removePairIndex + 1], 1, Qt::MatchRecursive);
694 QCOMPARE(idxs.size(), 1);
695
696 auto endIdx = idxs.at(0);
697
698 ModelRemoveCommand remove(&tree);
699 remove.setAncestorRowNumbers(tree.indexToPath(startIdx.parent()));
700 remove.setStartRow(startIdx.row());
701 remove.setEndRow(endIdx.row());
702 remove.doCommand();
703 }
704
705 QCOMPARE(beforeSpy.count(), spyCount);
706 QCOMPARE(afterSpy.count(), spyCount);
707
708 QCOMPARE(proxy.rowCount(), expectedRowCountAfter);
709 }
710
selectionMapping()711 void KSelectionProxyModelTest::selectionMapping()
712 {
713 QStringListModel strings(days);
714 QItemSelectionModel selectionModel(&strings);
715 KSelectionProxyModel proxy(&selectionModel);
716 proxy.setFilterBehavior(KSelectionProxyModel::SubTrees);
717 proxy.setSourceModel(&strings);
718 auto idx1 = strings.index(0, 0);
719 auto idx2 = strings.index(2, 0);
720 QItemSelection sourceSel;
721 sourceSel << QItemSelectionRange(idx1, idx2);
722 selectionModel.select(sourceSel, QItemSelectionModel::Select);
723
724 QItemSelection proxySel;
725 proxySel << QItemSelectionRange(proxy.index(0, 0), proxy.index(2, 0));
726
727 QCOMPARE(proxy.mapSelectionToSource(proxySel), sourceSel);
728 }
729
730 QTEST_MAIN(KSelectionProxyModelTest)
731
732 #include "kselectionproxymodeltest.moc"
733