]> cloud.milkyroute.net Git - dolphin.git/blob - src/tests/dolphintreeviewtest.cpp
Improve tests for DolphinTreeView's code that updates the selection
[dolphin.git] / src / tests / dolphintreeviewtest.cpp
1 /***************************************************************************
2 * Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include <qtest_kde.h>
21 #include <kdebug.h>
22
23 #include "views/dolphintreeview.h"
24
25 #include <qtestkeyboard.h>
26 #include <qtestmouse.h>
27 #include <QtGui/QStringListModel>
28
29 class DolphinTreeViewTest : public QObject
30 {
31 Q_OBJECT
32
33 private slots:
34
35 void testKeyboardNavigationSelectionUpdate();
36
37 void bug218114_visualRegionForSelection();
38
39 private:
40
41 /** A method that simplifies checking a view's current item and selection */
42 static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& expectedCurrent, const QModelIndexList& expectedSelection) {
43 QCOMPARE(view.currentIndex(), expectedCurrent);
44 const QModelIndexList selectedIndexes = view.selectionModel()->selectedIndexes();
45 QCOMPARE(selectedIndexes.count(), expectedSelection.count());
46 foreach(const QModelIndex& index, expectedSelection) {
47 QVERIFY(selectedIndexes.contains(index));
48 }
49 }
50
51 /** Use this method if only one item is selected */
52 static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& current, const QModelIndex& selected) {
53 QModelIndexList list;
54 list << selected;
55 verifyCurrentItemAndSelection(view, current, list);
56 }
57
58 /** Use this method if the only selected item is the current item */
59 static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& current) {
60 verifyCurrentItemAndSelection(view, current, current);
61 }
62
63 };
64
65 /**
66 * TestView is a simple view class derived from DolphinTreeView.
67 * It makes sure that the visualRect for each index contains only the item text as
68 * returned by QAbstractItemModel::data(...) for the role Qt::DisplayRole.
69 *
70 * We have to check that DolphinTreeView handles the case of visualRects with different widths
71 * correctly because this is the case in DolphinDetailsView which is derived from DolphinTreeView.
72 */
73
74 class TestView : public DolphinTreeView
75 {
76 Q_OBJECT
77
78 public:
79
80 TestView(QWidget* parent = 0) : DolphinTreeView(parent) {};
81 ~TestView() {};
82
83 QRect visualRect(const QModelIndex& index) const {
84 QRect rect = DolphinTreeView::visualRect(index);
85
86 const QStyleOptionViewItem option = viewOptions();
87 const QFontMetrics fontMetrics(option.font);
88 int width = option.decorationSize.width() + fontMetrics.width(model()->data(index).toString());
89
90 rect.setWidth(width);
91 return rect;
92 }
93
94 };
95
96 /**
97 * This test checks that updating the selection after key presses works as expected.
98 * Qt does not handle this internally if the first letter of an item is pressed, which
99 * is why DolphinTreeView has some custom code for this. The test verifies that this
100 * works without unwanted side effects.
101 *
102 * TODO: Add test for deletion of multiple files if Shift-Delete is pressed for some time, see
103 * https://bugs.kde.org/show_bug.cgi?id=259656
104 */
105
106 void DolphinTreeViewTest::testKeyboardNavigationSelectionUpdate() {
107 QStringList items;
108 items << "a" << "b" << "c" << "d" << "e";
109 QStringListModel model(items);
110
111 QModelIndex index[5];
112 for (int i = 0; i < 5; i++) {
113 index[i] = model.index(i, 0);
114 }
115
116 DolphinTreeView view;
117 view.setModel(&model);
118 view.setSelectionMode(QAbstractItemView::ExtendedSelection);
119 view.resize(400, 400);
120 view.show();
121 QTest::qWaitForWindowShown(&view);
122
123 view.clearSelection();
124 QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
125
126 /**
127 * Check that basic keyboard navigation with arrow keys works.
128 */
129
130 view.setCurrentIndex(index[0]);
131 verifyCurrentItemAndSelection(view, index[0]);
132
133 // Go down -> item 1 ("b") should be selected
134 kDebug() << "Down";
135 QTest::keyClick(view.viewport(), Qt::Key_Down);
136 verifyCurrentItemAndSelection(view, index[1]);
137
138 // Go down -> item 2 ("c") should be selected
139 kDebug() << "Down";
140 QTest::keyClick(view.viewport(), Qt::Key_Down);
141 verifyCurrentItemAndSelection(view, index[2]);
142
143 // Ctrl-Up -> item 2 ("c") remains selected
144 kDebug() << "Ctrl-Up";
145 QTest::keyClick(view.viewport(), Qt::Key_Up, Qt::ControlModifier);
146 verifyCurrentItemAndSelection(view, index[1], index[2]);
147
148 // Go up -> item 0 ("a") should be selected
149 kDebug() << "Up";
150 QTest::keyClick(view.viewport(), Qt::Key_Up);
151 verifyCurrentItemAndSelection(view, index[0]);
152
153 // Shift-Down -> items 0 and 1 ("a" and "b") should be selected
154 kDebug() << "Shift-Down";
155 QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
156 QModelIndexList expectedSelection;
157 expectedSelection << index[0] << index[1];
158 verifyCurrentItemAndSelection(view, index[1], expectedSelection);
159
160 /**
161 * When the first letter of a file name is pressed, this file becomes the current item
162 * and gets selected. If the user then Shift-clicks another item, it is expected that
163 * all items between these two items get selected. Before the bug
164 *
165 * https://bugs.kde.org/show_bug.cgi?id=201459
166 *
167 * was fixed, this was not the case: the starting point for the Shift-selection was not
168 * updated if an item was selected by pressing the first letter of the file name.
169 */
170
171 view.clearSelection();
172 QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
173
174 // Control-click item 0 ("a")
175 kDebug() << "Ctrl-click on \"a\"";
176 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.visualRect(index[0]).center());
177 verifyCurrentItemAndSelection(view, index[0]);
178
179 // Press "c", such that item 2 ("c") should be the current one.
180 kDebug() << "Press \"c\"";
181 QTest::keyClick(view.viewport(), Qt::Key_C);
182 verifyCurrentItemAndSelection(view, index[2]);
183
184 // Now Shift-Click the last item ("e"). We expect that 3 items ("c", "d", "e") are selected.
185 kDebug() << "Shift-click on \"e\"";
186 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(index[4]).center());
187 expectedSelection.clear();
188 expectedSelection << index[2] << index[3] << index[4];
189 verifyCurrentItemAndSelection(view, index[4], expectedSelection);
190
191 /**
192 * Starting a drag&drop operation should not clear the selection, see
193 *
194 * https://bugs.kde.org/show_bug.cgi?id=158649
195 */
196
197 view.clearSelection();
198 QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
199
200 // Click item 0 ("a")
201 kDebug() << "Click on \"a\"";
202 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
203 verifyCurrentItemAndSelection(view, index[0]);
204
205 // Shift-Down -> "a" and "b" should be selected
206 kDebug() << "Shift-Down";
207 QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
208 expectedSelection.clear();
209 expectedSelection << index[0] << index[1];
210 verifyCurrentItemAndSelection(view, index[1], expectedSelection);
211
212 // Press mouse button on item 0 ("a"), but do not release it. Check that the selection is unchanged
213 kDebug() << "Mouse press on \"a\"";
214 QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
215 verifyCurrentItemAndSelection(view, index[0], expectedSelection);
216
217 // Move mouse to item 1 ("b"), check that selection is unchanged
218 kDebug() << "Move mouse to \"b\"";
219 QMouseEvent moveEvent(QEvent::MouseMove, view.visualRect(index[1]).center(), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
220 bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
221 QVERIFY(moveEventReceived);
222 verifyCurrentItemAndSelection(view, index[0], expectedSelection);
223
224 // Release mouse button on item 1 ("b"), check that selection is unchanged
225 kDebug() << "Mouse release on \"b\"";
226 QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[1]).center());
227 verifyCurrentItemAndSelection(view, index[0], expectedSelection);
228 }
229
230 /**
231 * QTreeView assumes implicitly that the width of each item's visualRect is the same. This leads to painting
232 * problems in Dolphin if items with different widths are in one QItemSelectionRange, see
233 *
234 * https://bugs.kde.org/show_bug.cgi?id=218114
235 *
236 * To fix this, DolphinTreeView has a custom implementation of visualRegionForSelection(). The following
237 * unit test checks that.
238 */
239
240 void DolphinTreeViewTest::bug218114_visualRegionForSelection()
241 {
242 QStringList items;
243 items << "a" << "an item with a long name" << "a";
244 QStringListModel model(items);
245
246 QModelIndex index0 = model.index(0, 0);
247 QModelIndex index1 = model.index(1, 0);
248 QModelIndex index2 = model.index(2, 0);
249
250 TestView view;
251 view.setModel(&model);
252 view.setSelectionMode(QAbstractItemView::ExtendedSelection);
253 view.resize(400, 400);
254 view.show();
255 QTest::qWaitForWindowShown(&view);
256
257 // First check that the width of index1 is larger than that of index0 and index2 (this triggers the bug).
258
259 QVERIFY(view.visualRect(index0).width() < view.visualRect(index1).width());
260 QVERIFY(view.visualRect(index2).width() < view.visualRect(index1).width());
261
262 // Select all items in one go.
263
264 view.selectAll();
265 const QItemSelection selection = view.selectionModel()->selection();
266 QCOMPARE(selection.count(), 1);
267 QCOMPARE(selection.indexes().count(), 3);
268
269 // Verify that the visualRegionForSelection contains all visualRects.
270 // We do this indirectly using QRegion::boundingRect() because
271 // QRegion::contains(const QRect&) returns true even if the QRect is not
272 // entirely inside the QRegion.
273
274 const QRegion region = view.visualRegionForSelection(selection);
275 const QRect boundingRect = region.boundingRect();
276
277 QVERIFY(boundingRect.contains(view.visualRect(index0)));
278 QVERIFY(boundingRect.contains(view.visualRect(index1)));
279 QVERIFY(boundingRect.contains(view.visualRect(index2)));
280 }
281
282 QTEST_KDEMAIN(DolphinTreeViewTest, GUI)
283
284 #include "dolphintreeviewtest.moc"