1 /***************************************************************************
2 * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
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. *
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. *
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 ***************************************************************************/
20 #include <qtest_kde.h>
23 #include "kitemviews/kfileitemmodel.h"
27 const int DefaultTimeout
= 5000;
30 Q_DECLARE_METATYPE(KItemRangeList
)
32 class KFileItemModelTest
: public QObject
40 void testDefaultRoles();
41 void testDefaultSortRole();
42 void testDefaultGroupRole();
44 void testModelConsistencyWhenInsertingItems();
45 void testItemRangeConsistencyWhenInsertingItems();
46 void testExpandItems();
48 void testExpansionLevelsCompare_data();
49 void testExpansionLevelsCompare();
52 bool isModelConsistent() const;
55 KFileItemModel
* m_model
;
56 KDirLister
* m_dirLister
;
60 void KFileItemModelTest::init()
62 qRegisterMetaType
<KItemRangeList
>("KItemRangeList");
63 qRegisterMetaType
<KFileItemList
>("KFileItemList");
65 m_testDir
= new TestDir();
66 m_dirLister
= new KDirLister();
67 m_model
= new KFileItemModel(m_dirLister
);
70 void KFileItemModelTest::cleanup()
82 void KFileItemModelTest::testDefaultRoles()
84 const QSet
<QByteArray
> roles
= m_model
->roles();
85 QCOMPARE(roles
.count(), 2);
86 QVERIFY(roles
.contains("name"));
87 QVERIFY(roles
.contains("isDir"));
90 void KFileItemModelTest::testDefaultSortRole()
92 QCOMPARE(m_model
->sortRole(), QByteArray("name"));
95 files
<< "c.txt" << "a.txt" << "b.txt";
97 m_testDir
->createFiles(files
);
99 m_dirLister
->openUrl(m_testDir
->url());
100 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
102 QCOMPARE(m_model
->count(), 3);
103 QCOMPARE(m_model
->data(0)["name"].toString(), QString("a.txt"));
104 QCOMPARE(m_model
->data(1)["name"].toString(), QString("b.txt"));
105 QCOMPARE(m_model
->data(2)["name"].toString(), QString("c.txt"));
108 void KFileItemModelTest::testDefaultGroupRole()
110 QVERIFY(m_model
->groupRole().isEmpty());
113 void KFileItemModelTest::testNewItems()
116 files
<< "a.txt" << "b.txt" << "c.txt";
117 m_testDir
->createFiles(files
);
119 m_dirLister
->openUrl(m_testDir
->url());
120 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
122 QCOMPARE(m_model
->count(), 3);
124 QVERIFY(isModelConsistent());
127 void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
129 QSKIP("Temporary disabled", SkipSingle
);
131 // KFileItemModel prevents that inserting a punch of items sequentially
132 // results in an itemsInserted()-signal for each item. Instead internally
133 // a timeout is given that collects such operations and results in only
134 // one itemsInserted()-signal. However in this test we want to stress
135 // KFileItemModel to do a lot of insert operation and hence decrease
136 // the timeout to 1 millisecond.
137 m_model
->m_minimumUpdateIntervalTimer
->setInterval(1);
139 m_testDir
->createFile("1");
140 m_dirLister
->openUrl(m_testDir
->url());
141 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
142 QCOMPARE(m_model
->count(), 1);
144 // Insert 10 items for 20 times. After each insert operation the model consistency
146 QSet
<int> insertedItems
;
147 for (int i
= 0; i
< 20; ++i
) {
148 QSignalSpy
spy(m_model
, SIGNAL(itemsInserted(KItemRangeList
)));
150 for (int j
= 0; j
< 10; ++j
) {
151 int itemName
= qrand();
152 while (insertedItems
.contains(itemName
)) {
155 insertedItems
.insert(itemName
);
157 m_testDir
->createFile(QString::number(itemName
));
160 m_dirLister
->updateDirectory(m_testDir
->url());
161 if (spy
.count() == 0) {
162 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
165 QVERIFY(isModelConsistent());
168 QCOMPARE(m_model
->count(), 201);
171 void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
174 files
<< "B" << "E" << "G";
175 m_testDir
->createFiles(files
);
177 // Due to inserting the 3 items one item-range with index == 0 and
178 // count == 3 must be given
179 QSignalSpy
spy1(m_model
, SIGNAL(itemsInserted(KItemRangeList
)));
180 m_dirLister
->openUrl(m_testDir
->url());
181 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
183 QCOMPARE(spy1
.count(), 1);
184 QList
<QVariant
> arguments
= spy1
.takeFirst();
185 KItemRangeList itemRangeList
= arguments
.at(0).value
<KItemRangeList
>();
186 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(0, 3));
188 // The indexes of the item-ranges must always be related to the model before
189 // the items have been inserted. Having:
192 // and inserting A, C, D, F the resulting model will be:
195 // and the item-ranges must be:
196 // index: 0, count: 1 for A
197 // index: 1, count: 2 for B, C
198 // index: 2, count: 1 for G
201 files
<< "A" << "C" << "D" << "F";
202 m_testDir
->createFiles(files
);
204 QSignalSpy
spy2(m_model
, SIGNAL(itemsInserted(KItemRangeList
)));
205 m_dirLister
->updateDirectory(m_testDir
->url());
206 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
208 QCOMPARE(spy2
.count(), 1);
209 arguments
= spy2
.takeFirst();
210 itemRangeList
= arguments
.at(0).value
<KItemRangeList
>();
211 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(0, 1) << KItemRange(1, 2) << KItemRange(2, 1));
214 void KFileItemModelTest::testExpandItems()
216 // Test expanding subfolders in a folder with the items "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1".
217 // Besides testing the basic item expansion functionality, the test makes sure that
218 // KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b)
219 // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the
220 // first three characters.
221 QSet
<QByteArray
> modelRoles
= m_model
->roles();
222 modelRoles
<< "isExpanded" << "expansionLevel";
223 m_model
->setRoles(modelRoles
);
226 files
<< "a/a/1" << "a/a-1/1"; // missing folders are created automatically
227 m_testDir
->createFiles(files
);
229 m_dirLister
->openUrl(m_testDir
->url());
230 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
232 // So far, the model contains only "a/"
233 QCOMPARE(m_model
->count(), 1);
234 QVERIFY(m_model
->isExpandable(0));
235 QVERIFY(!m_model
->isExpanded(0));
237 QSignalSpy
spyInserted(m_model
, SIGNAL(itemsInserted(KItemRangeList
)));
239 // Expand the folder "a/" -> "a/a/" and "a/a-1/" become visible
240 m_model
->setExpanded(0, true);
241 QVERIFY(m_model
->isExpanded(0));
242 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
243 QCOMPARE(m_model
->count(), 3); // 3 items: "a/", "a/a/", "a/a-1/"
245 QCOMPARE(spyInserted
.count(), 1);
246 KItemRangeList itemRangeList
= spyInserted
.takeFirst().at(0).value
<KItemRangeList
>();
247 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(1, 2)); // 2 new items "a/a/" and "a/a-1/" with indices 1 and 2
249 QVERIFY(m_model
->isExpandable(1));
250 QVERIFY(!m_model
->isExpanded(1));
251 QVERIFY(m_model
->isExpandable(2));
252 QVERIFY(!m_model
->isExpanded(2));
254 // Expand the folder "a/a/" -> "a/a/1" becomes visible
255 m_model
->setExpanded(1, true);
256 QVERIFY(m_model
->isExpanded(1));
257 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
258 QCOMPARE(m_model
->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
260 QCOMPARE(spyInserted
.count(), 1);
261 itemRangeList
= spyInserted
.takeFirst().at(0).value
<KItemRangeList
>();
262 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(2, 1)); // 1 new item "a/a/1" with index 2
264 QVERIFY(!m_model
->isExpandable(2));
265 QVERIFY(!m_model
->isExpanded(2));
267 // Expand the folder "a/a-1/" -> "a/a-1/1" becomes visible
268 m_model
->setExpanded(3, true);
269 QVERIFY(m_model
->isExpanded(3));
270 QVERIFY(QTest::kWaitForSignal(m_model
, SIGNAL(itemsInserted(KItemRangeList
)), DefaultTimeout
));
271 QCOMPARE(m_model
->count(), 5); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
273 QCOMPARE(spyInserted
.count(), 1);
274 itemRangeList
= spyInserted
.takeFirst().at(0).value
<KItemRangeList
>();
275 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(4, 1)); // 1 new item "a/a-1/1" with index 4
277 QVERIFY(!m_model
->isExpandable(4));
278 QVERIFY(!m_model
->isExpanded(4));
280 QSignalSpy
spyRemoved(m_model
, SIGNAL(itemsRemoved(KItemRangeList
)));
282 // Collapse the top-level folder -> all other items should disappear
283 m_model
->setExpanded(0, false);
284 QVERIFY(!m_model
->isExpanded(0));
285 QCOMPARE(spyRemoved
.count(), 1);
286 itemRangeList
= spyRemoved
.takeFirst().at(0).value
<KItemRangeList
>();
287 QCOMPARE(itemRangeList
, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
290 void KFileItemModelTest::testExpansionLevelsCompare_data()
292 QTest::addColumn
<QString
>("urlA");
293 QTest::addColumn
<QString
>("urlB");
294 QTest::addColumn
<int>("result");
296 QTest::newRow("Equal") << "/a/b" << "/a/b" << 0;
297 QTest::newRow("Sub path: A < B") << "/a/b" << "/a/b/c" << -1;
298 QTest::newRow("Sub path: A > B") << "/a/b/c" << "/a/b" << +1;
299 QTest::newRow("Same level: /a/1 < /a-1/1") << "/a/1" << "/a-1/1" << -1;
300 QTest::newRow("Same level: /a-/1 > /a/1") << "/a-1/1" << "/a/1" << +1;
301 QTest::newRow("Different levels: /a/a/1 < /a/a-1") << "/a/a/1" << "/a/a-1" << -1;
302 QTest::newRow("Different levels: /a/a-1 > /a/a/1") << "/a/a-1" << "/a/a/1" << +1;
305 void KFileItemModelTest::testExpansionLevelsCompare()
307 QFETCH(QString
, urlA
);
308 QFETCH(QString
, urlB
);
311 const KFileItem
a(KUrl(urlA
), QString(), mode_t(-1));
312 const KFileItem
b(KUrl(urlB
), QString(), mode_t(-1));
313 QCOMPARE(m_model
->expansionLevelsCompare(a
, b
), result
);
316 bool KFileItemModelTest::isModelConsistent() const
318 for (int i
= 0; i
< m_model
->count(); ++i
) {
319 const KFileItem item
= m_model
->fileItem(i
);
321 qWarning() << "Item" << i
<< "is null";
325 const int itemIndex
= m_model
->index(item
);
326 if (itemIndex
!= i
) {
327 qWarning() << "Item" << i
<< "has a wrong index:" << itemIndex
;
335 QTEST_KDEMAIN(KFileItemModelTest
, NoGUI
)
337 #include "kfileitemmodeltest.moc"