1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
3 * Copyright (C) 2011 by Frank Reininghaus <frank78ac@googlemail.com> *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
19 ***************************************************************************/
21 #include <qtest_kde.h>
23 #include "kitemviews/kitemmodelbase.h"
24 #include "kitemviews/kitemlistselectionmanager.h"
26 class DummyModel
: public KItemModelBase
30 void setCount(int count
);
31 virtual int count() const;
32 virtual QHash
<QByteArray
, QVariant
> data(int index
) const;
38 DummyModel::DummyModel() :
44 void DummyModel::setCount(int count
)
49 int DummyModel::count() const
54 QHash
<QByteArray
, QVariant
> DummyModel::data(int index
) const
57 return QHash
<QByteArray
, QVariant
>();
61 class KItemListSelectionManagerTest
: public QObject
69 void testConstructor();
71 void testCurrentItemAnchorItem();
72 void testSetSelected_data();
73 void testSetSelected();
74 void testItemsInserted();
75 void testItemsRemoved();
76 void testAnchoredSelection();
77 void testChangeSelection_data();
78 void testChangeSelection();
79 void testDeleteCurrentItem_data();
80 void testDeleteCurrentItem();
83 void verifySelectionChange(QSignalSpy
& spy
, const QSet
<int>& currentSelection
, const QSet
<int>& previousSelection
) const;
85 KItemListSelectionManager
* m_selectionManager
;
89 void KItemListSelectionManagerTest::init()
91 m_model
= new DummyModel();
92 m_selectionManager
= new KItemListSelectionManager();
93 m_selectionManager
->setModel(m_model
);
96 void KItemListSelectionManagerTest::cleanup()
98 delete m_selectionManager
;
99 m_selectionManager
= 0;
105 void KItemListSelectionManagerTest::testConstructor()
107 QVERIFY(!m_selectionManager
->hasSelection());
108 QCOMPARE(m_selectionManager
->selectedItems().count(), 0);
109 QCOMPARE(m_selectionManager
->currentItem(), 0);
110 QCOMPARE(m_selectionManager
->m_anchorItem
, -1);
113 void KItemListSelectionManagerTest::testCurrentItemAnchorItem()
115 QSignalSpy
spyCurrent(m_selectionManager
, SIGNAL(currentChanged(int,int)));
117 // Set current item and check that the selection manager emits the currentChanged(int,int) signal correctly.
118 m_selectionManager
->setCurrentItem(4);
119 QCOMPARE(m_selectionManager
->currentItem(), 4);
120 QCOMPARE(spyCurrent
.count(), 1);
121 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(0)), 4);
122 spyCurrent
.takeFirst();
124 // Begin an anchored selection.
125 m_selectionManager
->beginAnchoredSelection(5);
126 QVERIFY(m_selectionManager
->isAnchoredSelectionActive());
127 QCOMPARE(m_selectionManager
->m_anchorItem
, 5);
129 // Items between current and anchor should be selected now
130 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 4 << 5);
131 QVERIFY(m_selectionManager
->hasSelection());
133 // Change current item again and check the selection
134 m_selectionManager
->setCurrentItem(2);
135 QCOMPARE(m_selectionManager
->currentItem(), 2);
136 QCOMPARE(spyCurrent
.count(), 1);
137 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(0)), 2);
138 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(1)), 4);
139 spyCurrent
.takeFirst();
141 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 2 << 3 << 4 << 5);
142 QVERIFY(m_selectionManager
->hasSelection());
144 // Inserting items should update current item and anchor item.
145 m_selectionManager
->itemsInserted(KItemRangeList() <<
150 QCOMPARE(m_selectionManager
->currentItem(), 5);
151 QCOMPARE(spyCurrent
.count(), 1);
152 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(0)), 5);
153 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(1)), 2);
154 spyCurrent
.takeFirst();
156 QCOMPARE(m_selectionManager
->m_anchorItem
, 8);
158 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7 << 8);
159 QVERIFY(m_selectionManager
->hasSelection());
161 // Removing items should update current item and anchor item.
162 m_selectionManager
->itemsRemoved(KItemRangeList() <<
167 QCOMPARE(m_selectionManager
->currentItem(), 2);
168 QCOMPARE(spyCurrent
.count(), 1);
169 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(0)), 2);
170 QCOMPARE(qvariant_cast
<int>(spyCurrent
.at(0).at(1)), 5);
171 spyCurrent
.takeFirst();
173 QCOMPARE(m_selectionManager
->m_anchorItem
, 5);
175 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 2 << 3 << 4 << 5);
176 QVERIFY(m_selectionManager
->hasSelection());
178 // Verify that clearSelection() also clears the anchored selection.
179 m_selectionManager
->clearSelection();
180 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>());
181 QVERIFY(!m_selectionManager
->hasSelection());
183 m_selectionManager
->endAnchoredSelection();
184 QVERIFY(!m_selectionManager
->isAnchoredSelectionActive());
187 void KItemListSelectionManagerTest::testSetSelected_data()
189 QTest::addColumn
<int>("index");
190 QTest::addColumn
<int>("count");
191 QTest::addColumn
<int>("expectedSelectionCount");
193 QTest::newRow("Select all") << 0 << 100 << 100;
194 QTest::newRow("Sub selection 15 items") << 20 << 15 << 15;
195 QTest::newRow("Sub selection 1 item") << 20 << 1 << 1;
196 QTest::newRow("Too small index") << -1 << 100 << 0;
197 QTest::newRow("Too large index") << 100 << 100 << 0;
198 QTest::newRow("Too large count") << 0 << 100000 << 100;
199 QTest::newRow("Too small count") << 0 << 0 << 0;
202 void KItemListSelectionManagerTest::testSetSelected()
206 QFETCH(int, expectedSelectionCount
);
207 m_selectionManager
->setSelected(index
, count
);
208 QCOMPARE(m_selectionManager
->selectedItems().count(), expectedSelectionCount
);
211 void KItemListSelectionManagerTest::testItemsInserted()
213 // Select items 10 to 12
214 m_selectionManager
->setSelected(10, 3);
215 QSet
<int> selectedItems
= m_selectionManager
->selectedItems();
216 QCOMPARE(selectedItems
.count(), 3);
217 QVERIFY(selectedItems
.contains(10));
218 QVERIFY(selectedItems
.contains(11));
219 QVERIFY(selectedItems
.contains(12));
221 // Insert items 0 to 4 -> selection must be 15 to 17
222 m_selectionManager
->itemsInserted(KItemRangeList() << KItemRange(0, 5));
223 selectedItems
= m_selectionManager
->selectedItems();
224 QCOMPARE(selectedItems
.count(), 3);
225 QVERIFY(selectedItems
.contains(15));
226 QVERIFY(selectedItems
.contains(16));
227 QVERIFY(selectedItems
.contains(17));
229 // Insert 3 items between the selections
230 m_selectionManager
->itemsInserted(KItemRangeList() <<
234 selectedItems
= m_selectionManager
->selectedItems();
235 QCOMPARE(selectedItems
.count(), 3);
236 QVERIFY(selectedItems
.contains(16));
237 QVERIFY(selectedItems
.contains(18));
238 QVERIFY(selectedItems
.contains(20));
241 void KItemListSelectionManagerTest::testItemsRemoved()
243 // Select items 10 to 15
244 m_selectionManager
->setSelected(10, 6);
245 QSet
<int> selectedItems
= m_selectionManager
->selectedItems();
246 QCOMPARE(selectedItems
.count(), 6);
247 for (int i
= 10; i
<= 15; ++i
) {
248 QVERIFY(selectedItems
.contains(i
));
251 // Remove items 0 to 4 -> selection must be 5 to 10
252 m_selectionManager
->itemsRemoved(KItemRangeList() << KItemRange(0, 5));
253 selectedItems
= m_selectionManager
->selectedItems();
254 QCOMPARE(selectedItems
.count(), 6);
255 for (int i
= 5; i
<= 10; ++i
) {
256 QVERIFY(selectedItems
.contains(i
));
259 // Remove the items 6 , 8 and 10
260 m_selectionManager
->itemsRemoved(KItemRangeList() <<
264 selectedItems
= m_selectionManager
->selectedItems();
265 QCOMPARE(selectedItems
.count(), 3);
266 QVERIFY(selectedItems
.contains(5));
267 QVERIFY(selectedItems
.contains(6));
268 QVERIFY(selectedItems
.contains(7));
271 void KItemListSelectionManagerTest::testAnchoredSelection()
273 m_selectionManager
->beginAnchoredSelection(5);
274 QVERIFY(m_selectionManager
->isAnchoredSelectionActive());
275 QCOMPARE(m_selectionManager
->m_anchorItem
, 5);
277 m_selectionManager
->setCurrentItem(6);
278 QCOMPARE(m_selectionManager
->currentItem(), 6);
279 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6);
281 m_selectionManager
->setCurrentItem(4);
282 QCOMPARE(m_selectionManager
->currentItem(), 4);
283 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 4 << 5);
285 m_selectionManager
->setCurrentItem(7);
286 QCOMPARE(m_selectionManager
->currentItem(), 7);
287 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7);
289 // Ending the anchored selection should not change the selected items.
290 m_selectionManager
->endAnchoredSelection();
291 QVERIFY(!m_selectionManager
->isAnchoredSelectionActive());
292 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7);
294 // Start a new anchored selection that overlaps the previous one
295 m_selectionManager
->beginAnchoredSelection(9);
296 QVERIFY(m_selectionManager
->isAnchoredSelectionActive());
297 QCOMPARE(m_selectionManager
->m_anchorItem
, 9);
299 m_selectionManager
->setCurrentItem(6);
300 QCOMPARE(m_selectionManager
->currentItem(), 6);
301 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7 << 8 << 9);
303 m_selectionManager
->setCurrentItem(10);
304 QCOMPARE(m_selectionManager
->currentItem(), 10);
305 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7 << 9 << 10);
307 m_selectionManager
->endAnchoredSelection();
308 QVERIFY(!m_selectionManager
->isAnchoredSelectionActive());
309 QCOMPARE(m_selectionManager
->selectedItems(), QSet
<int>() << 5 << 6 << 7 << 9 << 10);
318 EndAnchoredSelection
,
323 Q_DECLARE_METATYPE(QSet
<int>);
324 Q_DECLARE_METATYPE(ChangeType
);
325 Q_DECLARE_METATYPE(KItemRange
);
326 Q_DECLARE_METATYPE(KItemRangeList
);
327 Q_DECLARE_METATYPE(KItemListSelectionManager::SelectionMode
);
328 Q_DECLARE_METATYPE(QList
<int>);
331 * The following function provides a generic way to test the selection functionality.
333 * The test is data-driven and takes the following arguments:
335 * \param initialSelection The selection at the beginning.
336 * \param anchor This item will be the anchor item.
337 * \param current This item will be the current item.
338 * \param expectedSelection Expected selection after anchor and current are set.
339 * \param changeType Type of the change that is done then:
341 * - InsertItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsInserted()
342 * - RemoveItems -> data.at(0) provides the KItemRangeList. \sa KItemListSelectionManager::itemsRemoved()
343 * - MoveItems -> data.at(0) provides the KItemRange containing the original indices,
344 * data.at(1) provides the list containing the new indices
345 * \sa KItemListSelectionManager::itemsMoved(), KItemModelBase::itemsMoved()
346 * - EndAnchoredSelection
347 * - SetSelected -> data.at(0) provides the index where the selection process starts,
348 * data.at(1) provides the number of indices to be selected,
349 * data.at(2) provides the selection mode.
350 * \sa KItemListSelectionManager::setSelected()
351 * \param data A list of QVariants which will be cast to the arguments needed for the chosen ChangeType (see above).
352 * \param finalSelection The expected final selection.
356 void KItemListSelectionManagerTest::testChangeSelection_data()
358 QTest::addColumn
<QSet
<int> >("initialSelection");
359 QTest::addColumn
<int>("anchor");
360 QTest::addColumn
<int>("current");
361 QTest::addColumn
<QSet
<int> >("expectedSelection");
362 QTest::addColumn
<ChangeType
>("changeType");
363 QTest::addColumn
<QList
<QVariant
> >("data");
364 QTest::addColumn
<QSet
<int> >("finalSelection");
366 QTest::newRow("No change")
367 << (QSet
<int>() << 5 << 6)
369 << (QSet
<int>() << 2 << 3 << 5 << 6)
372 << (QSet
<int>() << 2 << 3 << 5 << 6);
374 QTest::newRow("Insert Items")
375 << (QSet
<int>() << 5 << 6)
377 << (QSet
<int>() << 2 << 3 << 5 << 6)
379 << (QList
<QVariant
>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2) << KItemRange(10, 5)))
380 << (QSet
<int>() << 3 << 4 << 8 << 9);
382 QTest::newRow("Remove Items")
383 << (QSet
<int>() << 5 << 6)
385 << (QSet
<int>() << 2 << 3 << 5 << 6)
387 << (QList
<QVariant
>() << QVariant::fromValue(KItemRangeList() << KItemRange(1, 1) << KItemRange(3, 1) << KItemRange(10, 5)))
388 << (QSet
<int>() << 1 << 2 << 3 << 4);
390 QTest::newRow("Empty Anchored Selection")
394 << EndAnchoredSelection
398 QTest::newRow("Toggle selection")
399 << (QSet
<int>() << 1 << 3 << 4)
401 << (QSet
<int>() << 1 << 3 << 4 << 6 << 7 << 8)
403 << (QList
<QVariant
>() << 0 << 10 << QVariant::fromValue(KItemListSelectionManager::Toggle
))
404 << (QSet
<int>() << 0 << 2 << 5 << 9);
406 // Swap items 2, 3 and 4, 5
407 QTest::newRow("Move items")
408 << (QSet
<int>() << 0 << 1 << 2 << 3)
410 << (QSet
<int>() << 0 << 1 << 2 << 3)
412 << (QList
<QVariant
>() << QVariant::fromValue(KItemRange(2, 4))
413 << QVariant::fromValue(QList
<int>() << 4 << 5 << 2 << 3))
414 << (QSet
<int>() << 0 << 1 << 4 << 5);
417 QTest::newRow("Revert sort order")
418 << (QSet
<int>() << 0 << 1)
420 << (QSet
<int>() << 0 << 1 << 3 << 4)
422 << (QList
<QVariant
>() << QVariant::fromValue(KItemRange(0, 10))
423 << QVariant::fromValue(QList
<int>() << 9 << 8 << 7 << 6 << 5 << 4 << 3 << 2 << 1 << 0))
424 << (QSet
<int>() << 5 << 6 << 8 << 9);
427 void KItemListSelectionManagerTest::testChangeSelection()
429 QFETCH(QSet
<int>, initialSelection
);
431 QFETCH(int, current
);
432 QFETCH(QSet
<int>, expectedSelection
);
433 QFETCH(ChangeType
, changeType
);
434 QFETCH(QList
<QVariant
>, data
);
435 QFETCH(QSet
<int>, finalSelection
);
437 QSignalSpy
spySelectionChanged(m_selectionManager
, SIGNAL(selectionChanged(QSet
<int>,QSet
<int>)));
439 // Initial selection should be empty
440 QVERIFY(!m_selectionManager
->hasSelection());
441 QVERIFY(m_selectionManager
->selectedItems().isEmpty());
443 // Perform the initial selectiion
444 m_selectionManager
->setSelectedItems(initialSelection
);
446 verifySelectionChange(spySelectionChanged
, initialSelection
, QSet
<int>());
448 // Perform an anchored selection.
449 // Note that current and anchor index are equal first because this is the case in typical uses of the
450 // selection manager, and because this makes it easier to test the correctness of the signal's arguments.
451 m_selectionManager
->setCurrentItem(anchor
);
452 m_selectionManager
->beginAnchoredSelection(anchor
);
453 m_selectionManager
->setCurrentItem(current
);
454 QCOMPARE(m_selectionManager
->m_anchorItem
, anchor
);
455 QCOMPARE(m_selectionManager
->currentItem(), current
);
457 verifySelectionChange(spySelectionChanged
, expectedSelection
, initialSelection
);
459 // Change the model by inserting or removing items.
460 switch (changeType
) {
462 m_selectionManager
->itemsInserted(data
.at(0).value
<KItemRangeList
>());
465 m_selectionManager
->itemsRemoved(data
.at(0).value
<KItemRangeList
>());
468 m_selectionManager
->itemsMoved(data
.at(0).value
<KItemRange
>(),
469 data
.at(1).value
<QList
<int> >());
471 case EndAnchoredSelection
:
472 m_selectionManager
->endAnchoredSelection();
473 QVERIFY(!m_selectionManager
->isAnchoredSelectionActive());
476 m_selectionManager
->setSelected(data
.at(0).value
<int>(), // index
477 data
.at(1).value
<int>(), // count
478 data
.at(2).value
<KItemListSelectionManager::SelectionMode
>());
484 verifySelectionChange(spySelectionChanged
, finalSelection
, expectedSelection
);
486 // Finally, clear the selection
487 m_selectionManager
->clearSelection();
489 verifySelectionChange(spySelectionChanged
, QSet
<int>(), finalSelection
);
492 void KItemListSelectionManagerTest::testDeleteCurrentItem_data()
494 QTest::addColumn
<int>("oldCurrentItemIndex");
495 QTest::addColumn
<int>("removeIndex");
496 QTest::addColumn
<int>("removeCount");
497 QTest::addColumn
<int>("newCurrentItemIndex");
499 QTest::newRow("Remove before") << 50 << 0 << 10 << 40;
500 QTest::newRow("Remove after") << 50 << 51 << 10 << 50;
501 QTest::newRow("Remove exactly current item") << 50 << 50 << 1 << 50;
502 QTest::newRow("Remove around current item") << 50 << 45 << 10 << 50;
503 QTest::newRow("Remove all except one item") << 50 << 1 << 99 << 0;
506 void KItemListSelectionManagerTest::testDeleteCurrentItem()
508 QFETCH(int, oldCurrentItemIndex
);
509 QFETCH(int, removeIndex
);
510 QFETCH(int, removeCount
);
511 QFETCH(int, newCurrentItemIndex
);
513 m_selectionManager
->setCurrentItem(oldCurrentItemIndex
);
515 const int newCount
= m_model
->count() - removeCount
;
516 m_model
->setCount(newCount
);
517 m_selectionManager
->itemsRemoved(KItemRangeList() << KItemRange(removeIndex
, removeCount
));
519 QCOMPARE(m_selectionManager
->currentItem(), newCurrentItemIndex
);
522 void KItemListSelectionManagerTest::verifySelectionChange(QSignalSpy
& spy
,
523 const QSet
<int>& currentSelection
,
524 const QSet
<int>& previousSelection
) const
526 QCOMPARE(m_selectionManager
->selectedItems(), currentSelection
);
527 QCOMPARE(m_selectionManager
->hasSelection(), !currentSelection
.isEmpty());
528 for (int index
= 0; index
< m_selectionManager
->model()->count(); ++index
) {
529 if (currentSelection
.contains(index
)) {
530 QVERIFY(m_selectionManager
->isSelected(index
));
533 QVERIFY(!m_selectionManager
->isSelected(index
));
537 if (currentSelection
== previousSelection
) {
538 QCOMPARE(spy
.count(), 0);
541 QCOMPARE(spy
.count(), 1);
542 QList
<QVariant
> arguments
= spy
.takeFirst();
543 QCOMPARE(qvariant_cast
<QSet
<int> >(arguments
.at(0)), currentSelection
);
544 QCOMPARE(qvariant_cast
<QSet
<int> >(arguments
.at(1)), previousSelection
);
548 QTEST_KDEMAIN(KItemListSelectionManagerTest
, NoGUI
)
550 #include "kitemlistselectionmanagertest.moc"