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>
24
#include "kitemviews/kfileitemmodel.h"
27
void myMessageOutput(QtMsgType type, const char* msg)
35
fprintf(stderr, "Critical: %s\n", msg);
38
fprintf(stderr, "Fatal: %s\n", msg);
46
const int DefaultTimeout = 5000;
49
Q_DECLARE_METATYPE(KItemRangeList)
50
Q_DECLARE_METATYPE(QList<int>)
52
class KFileItemModelTest : public QObject
60
void testDefaultRoles();
61
void testDefaultSortRole();
62
void testDefaultGroupedSorting();
64
void testRemoveItems();
65
void testLoadingCompleted();
67
void testSetDataWithModifiedSortRole_data();
68
void testSetDataWithModifiedSortRole();
69
void testModelConsistencyWhenInsertingItems();
70
void testItemRangeConsistencyWhenInsertingItems();
71
void testExpandItems();
72
void testExpandParentItems();
75
void testIndexForKeyboardSearch();
77
void testNameFilter();
80
bool isModelConsistent() const;
81
QStringList itemsInModel() const;
84
KFileItemModel* m_model;
85
KDirLister* m_dirLister;
89
void KFileItemModelTest::init()
91
// The item-model tests result in a huge number of debugging
92
// output from kdelibs. Only show critical and fatal messages.
93
qInstallMsgHandler(myMessageOutput);
95
qRegisterMetaType<KItemRange>("KItemRange");
96
qRegisterMetaType<KItemRangeList>("KItemRangeList");
97
qRegisterMetaType<KFileItemList>("KFileItemList");
99
m_testDir = new TestDir();
100
m_dirLister = new KDirLister();
101
m_dirLister->setAutoUpdate(false);
102
m_model = new KFileItemModel(m_dirLister);
105
void KFileItemModelTest::cleanup()
117
void KFileItemModelTest::testDefaultRoles()
119
const QSet<QByteArray> roles = m_model->roles();
120
QCOMPARE(roles.count(), 2);
121
QVERIFY(roles.contains("name"));
122
QVERIFY(roles.contains("isDir"));
125
void KFileItemModelTest::testDefaultSortRole()
127
QCOMPARE(m_model->sortRole(), QByteArray("name"));
130
files << "c.txt" << "a.txt" << "b.txt";
132
m_testDir->createFiles(files);
134
m_dirLister->openUrl(m_testDir->url());
135
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
137
QCOMPARE(m_model->count(), 3);
138
QCOMPARE(m_model->data(0)["name"].toString(), QString("a.txt"));
139
QCOMPARE(m_model->data(1)["name"].toString(), QString("b.txt"));
140
QCOMPARE(m_model->data(2)["name"].toString(), QString("c.txt"));
143
void KFileItemModelTest::testDefaultGroupedSorting()
145
QCOMPARE(m_model->groupedSorting(), false);
148
void KFileItemModelTest::testNewItems()
151
files << "a.txt" << "b.txt" << "c.txt";
152
m_testDir->createFiles(files);
154
m_dirLister->openUrl(m_testDir->url());
155
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
157
QCOMPARE(m_model->count(), 3);
159
QVERIFY(isModelConsistent());
162
void KFileItemModelTest::testRemoveItems()
164
m_testDir->createFile("a.txt");
165
m_testDir->createFile("b.txt");
166
m_dirLister->openUrl(m_testDir->url());
167
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
168
QCOMPARE(m_model->count(), 2);
169
QVERIFY(isModelConsistent());
171
m_testDir->removeFile("a.txt");
172
m_dirLister->updateDirectory(m_testDir->url());
173
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
174
QCOMPARE(m_model->count(), 1);
175
QVERIFY(isModelConsistent());
178
void KFileItemModelTest::testLoadingCompleted()
180
QSignalSpy loadingCompletedSpy(m_model, SIGNAL(loadingCompleted()));
181
QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
182
QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
184
m_testDir->createFiles(QStringList() << "a.txt" << "b.txt" << "c.txt");
186
m_dirLister->openUrl(m_testDir->url());
187
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
188
QCOMPARE(loadingCompletedSpy.count(), 1);
189
QCOMPARE(itemsInsertedSpy.count(), 1);
190
QCOMPARE(itemsRemovedSpy.count(), 0);
191
QCOMPARE(m_model->count(), 3);
193
m_testDir->createFiles(QStringList() << "d.txt" << "e.txt");
194
m_dirLister->updateDirectory(m_testDir->url());
195
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
196
QCOMPARE(loadingCompletedSpy.count(), 2);
197
QCOMPARE(itemsInsertedSpy.count(), 2);
198
QCOMPARE(itemsRemovedSpy.count(), 0);
199
QCOMPARE(m_model->count(), 5);
201
m_testDir->removeFile("a.txt");
202
m_testDir->createFile("f.txt");
203
m_dirLister->updateDirectory(m_testDir->url());
204
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
205
QCOMPARE(loadingCompletedSpy.count(), 3);
206
QCOMPARE(itemsInsertedSpy.count(), 3);
207
QCOMPARE(itemsRemovedSpy.count(), 1);
208
QCOMPARE(m_model->count(), 5);
210
m_testDir->removeFile("b.txt");
211
m_dirLister->updateDirectory(m_testDir->url());
212
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
213
QCOMPARE(loadingCompletedSpy.count(), 4);
214
QCOMPARE(itemsInsertedSpy.count(), 3);
215
QCOMPARE(itemsRemovedSpy.count(), 2);
216
QCOMPARE(m_model->count(), 4);
218
QVERIFY(isModelConsistent());
221
void KFileItemModelTest::testSetData()
223
m_testDir->createFile("a.txt");
225
m_dirLister->openUrl(m_testDir->url());
226
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
228
QHash<QByteArray, QVariant> values;
229
values.insert("customRole1", "Test1");
230
values.insert("customRole2", "Test2");
232
QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)));
233
m_model->setData(0, values);
234
QCOMPARE(itemsChangedSpy.count(), 1);
236
values = m_model->data(0);
237
QCOMPARE(values.value("customRole1").toString(), QString("Test1"));
238
QCOMPARE(values.value("customRole2").toString(), QString("Test2"));
239
QVERIFY(isModelConsistent());
242
void KFileItemModelTest::testSetDataWithModifiedSortRole_data()
244
QTest::addColumn<int>("changedIndex");
245
QTest::addColumn<int>("changedRating");
246
QTest::addColumn<bool>("expectMoveSignal");
247
QTest::addColumn<int>("ratingIndex0");
248
QTest::addColumn<int>("ratingIndex1");
249
QTest::addColumn<int>("ratingIndex2");
252
// Index 0 = rating 2
253
// Index 1 = rating 4
254
// Index 2 = rating 6
256
QTest::newRow("Index 0: Rating 3") << 0 << 3 << false << 3 << 4 << 6;
257
QTest::newRow("Index 0: Rating 5") << 0 << 5 << true << 4 << 5 << 6;
258
QTest::newRow("Index 0: Rating 8") << 0 << 8 << true << 4 << 6 << 8;
260
QTest::newRow("Index 2: Rating 1") << 2 << 1 << true << 1 << 2 << 4;
261
QTest::newRow("Index 2: Rating 3") << 2 << 3 << true << 2 << 3 << 4;
262
QTest::newRow("Index 2: Rating 5") << 2 << 5 << false << 2 << 4 << 5;
265
void KFileItemModelTest::testSetDataWithModifiedSortRole()
267
QFETCH(int, changedIndex);
268
QFETCH(int, changedRating);
269
QFETCH(bool, expectMoveSignal);
270
QFETCH(int, ratingIndex0);
271
QFETCH(int, ratingIndex1);
272
QFETCH(int, ratingIndex2);
274
// Changing the value of a sort-role must result in
275
// a reordering of the items.
276
QCOMPARE(m_model->sortRole(), QByteArray("name"));
279
files << "a.txt" << "b.txt" << "c.txt";
280
m_testDir->createFiles(files);
282
m_dirLister->openUrl(m_testDir->url());
283
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
285
// Fill the "rating" role of each file:
290
QHash<QByteArray, QVariant> ratingA;
291
ratingA.insert("rating", 2);
292
m_model->setData(0, ratingA);
294
QHash<QByteArray, QVariant> ratingB;
295
ratingB.insert("rating", 4);
296
m_model->setData(1, ratingB);
298
QHash<QByteArray, QVariant> ratingC;
299
ratingC.insert("rating", 6);
300
m_model->setData(2, ratingC);
302
m_model->setSortRole("rating");
303
QCOMPARE(m_model->data(0).value("rating").toInt(), 2);
304
QCOMPARE(m_model->data(1).value("rating").toInt(), 4);
305
QCOMPARE(m_model->data(2).value("rating").toInt(), 6);
307
// Now change the rating from a.txt. This usually results
308
// in reordering of the items.
309
QHash<QByteArray, QVariant> rating;
310
rating.insert("rating", changedRating);
311
m_model->setData(changedIndex, rating);
313
if (expectMoveSignal) {
314
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
317
QCOMPARE(m_model->data(0).value("rating").toInt(), ratingIndex0);
318
QCOMPARE(m_model->data(1).value("rating").toInt(), ratingIndex1);
319
QCOMPARE(m_model->data(2).value("rating").toInt(), ratingIndex2);
320
QVERIFY(isModelConsistent());
323
void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
325
//QSKIP("Temporary disabled", SkipSingle);
327
// KFileItemModel prevents that inserting a punch of items sequentially
328
// results in an itemsInserted()-signal for each item. Instead internally
329
// a timeout is given that collects such operations and results in only
330
// one itemsInserted()-signal. However in this test we want to stress
331
// KFileItemModel to do a lot of insert operation and hence decrease
332
// the timeout to 1 millisecond.
333
m_testDir->createFile("1");
334
m_dirLister->openUrl(m_testDir->url());
335
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
336
QCOMPARE(m_model->count(), 1);
338
// Insert 10 items for 20 times. After each insert operation the model consistency
340
QSet<int> insertedItems;
341
for (int i = 0; i < 20; ++i) {
342
QSignalSpy spy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
344
for (int j = 0; j < 10; ++j) {
345
int itemName = qrand();
346
while (insertedItems.contains(itemName)) {
349
insertedItems.insert(itemName);
351
m_testDir->createFile(QString::number(itemName));
354
m_dirLister->updateDirectory(m_testDir->url());
355
if (spy.count() == 0) {
356
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
359
QVERIFY(isModelConsistent());
362
QCOMPARE(m_model->count(), 201);
365
void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
368
files << "B" << "E" << "G";
369
m_testDir->createFiles(files);
371
// Due to inserting the 3 items one item-range with index == 0 and
372
// count == 3 must be given
373
QSignalSpy spy1(m_model, SIGNAL(itemsInserted(KItemRangeList)));
374
m_dirLister->openUrl(m_testDir->url());
375
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
377
QCOMPARE(spy1.count(), 1);
378
QList<QVariant> arguments = spy1.takeFirst();
379
KItemRangeList itemRangeList = arguments.at(0).value<KItemRangeList>();
380
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 3));
382
// The indexes of the item-ranges must always be related to the model before
383
// the items have been inserted. Having:
386
// and inserting A, C, D, F the resulting model will be:
389
// and the item-ranges must be:
390
// index: 0, count: 1 for A
391
// index: 1, count: 2 for B, C
392
// index: 2, count: 1 for G
395
files << "A" << "C" << "D" << "F";
396
m_testDir->createFiles(files);
398
QSignalSpy spy2(m_model, SIGNAL(itemsInserted(KItemRangeList)));
399
m_dirLister->updateDirectory(m_testDir->url());
400
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
402
QCOMPARE(spy2.count(), 1);
403
arguments = spy2.takeFirst();
404
itemRangeList = arguments.at(0).value<KItemRangeList>();
405
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 1) << KItemRange(1, 2) << KItemRange(2, 1));
408
void KFileItemModelTest::testExpandItems()
410
// Test expanding subfolders in a folder with the items "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1".
411
// Besides testing the basic item expansion functionality, the test makes sure that
412
// KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b)
413
// yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the
414
// first three characters.
415
QSet<QByteArray> modelRoles = m_model->roles();
416
modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
417
m_model->setRoles(modelRoles);
420
files << "a/a/1" << "a/a-1/1"; // missing folders are created automatically
421
m_testDir->createFiles(files);
423
// Store the URLs of all folders in a set.
424
QSet<KUrl> allFolders;
425
allFolders << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a") << KUrl(m_testDir->name() + "a/a-1");
427
m_dirLister->openUrl(m_testDir->url());
428
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
430
// So far, the model contains only "a/"
431
QCOMPARE(m_model->count(), 1);
432
QVERIFY(m_model->isExpandable(0));
433
QVERIFY(!m_model->isExpanded(0));
434
QVERIFY(m_model->expandedUrls().empty());
436
QSignalSpy spyInserted(m_model, SIGNAL(itemsInserted(KItemRangeList)));
438
// Expand the folder "a/" -> "a/a/" and "a/a-1/" become visible
439
m_model->setExpanded(0, true);
440
QVERIFY(m_model->isExpanded(0));
441
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
442
QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/a/", "a/a-1/"
443
QCOMPARE(m_model->expandedUrls(), QSet<KUrl>() << KUrl(m_testDir->name() + "a"));
445
QCOMPARE(spyInserted.count(), 1);
446
KItemRangeList itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
447
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2)); // 2 new items "a/a/" and "a/a-1/" with indices 1 and 2
449
QVERIFY(m_model->isExpandable(1));
450
QVERIFY(!m_model->isExpanded(1));
451
QVERIFY(m_model->isExpandable(2));
452
QVERIFY(!m_model->isExpanded(2));
454
// Expand the folder "a/a/" -> "a/a/1" becomes visible
455
m_model->setExpanded(1, true);
456
QVERIFY(m_model->isExpanded(1));
457
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
458
QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
459
QCOMPARE(m_model->expandedUrls(), QSet<KUrl>() << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a"));
461
QCOMPARE(spyInserted.count(), 1);
462
itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
463
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 1)); // 1 new item "a/a/1" with index 2
465
QVERIFY(!m_model->isExpandable(2));
466
QVERIFY(!m_model->isExpanded(2));
468
// Expand the folder "a/a-1/" -> "a/a-1/1" becomes visible
469
m_model->setExpanded(3, true);
470
QVERIFY(m_model->isExpanded(3));
471
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
472
QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
473
QCOMPARE(m_model->expandedUrls(), allFolders);
475
QCOMPARE(spyInserted.count(), 1);
476
itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
477
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(4, 1)); // 1 new item "a/a-1/1" with index 4
479
QVERIFY(!m_model->isExpandable(4));
480
QVERIFY(!m_model->isExpanded(4));
482
QSignalSpy spyRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
484
// Collapse the top-level folder -> all other items should disappear
485
m_model->setExpanded(0, false);
486
QVERIFY(!m_model->isExpanded(0));
487
QCOMPARE(m_model->count(), 1);
488
QVERIFY(!m_model->expandedUrls().contains(KUrl(m_testDir->name() + "a"))); // TODO: Make sure that child URLs are also removed
490
QCOMPARE(spyRemoved.count(), 1);
491
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
492
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
494
// Clear the model, reload the folder and try to restore the expanded folders.
496
QCOMPARE(m_model->count(), 0);
497
QVERIFY(m_model->expandedUrls().empty());
499
m_dirLister->openUrl(m_testDir->url());
500
m_model->restoreExpandedUrls(allFolders);
501
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
502
QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
503
QVERIFY(m_model->isExpanded(0));
504
QVERIFY(m_model->isExpanded(1));
505
QVERIFY(!m_model->isExpanded(2));
506
QVERIFY(m_model->isExpanded(3));
507
QVERIFY(!m_model->isExpanded(4));
508
QCOMPARE(m_model->expandedUrls(), allFolders);
510
// Move to a sub folder, then call restoreExpandedFolders() *before* going back.
511
// This is how DolphinView restores the expanded folders when navigating in history.
512
m_dirLister->openUrl(KUrl(m_testDir->name() + "a/a/"));
513
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
514
QCOMPARE(m_model->count(), 1); // 1 item: "1"
515
m_model->restoreExpandedUrls(allFolders);
516
m_dirLister->openUrl(m_testDir->url());
517
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
518
QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
519
QCOMPARE(m_model->expandedUrls(), allFolders);
522
void KFileItemModelTest::testExpandParentItems()
524
// Create a tree structure of folders:
532
QSet<QByteArray> modelRoles = m_model->roles();
533
modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
534
m_model->setRoles(modelRoles);
537
files << "a 1/b1/c1/file.txt" << "a2/b2/c2/d2/file.txt"; // missing folders are created automatically
538
m_testDir->createFiles(files);
540
m_dirLister->openUrl(m_testDir->url());
541
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
543
// So far, the model contains only "a 1/" and "a2/".
544
QCOMPARE(m_model->count(), 2);
545
QVERIFY(m_model->expandedUrls().empty());
547
// Expand the parents of "a2/b2/c2".
548
m_model->expandParentItems(KUrl(m_testDir->name() + "a2/b2/c2"));
549
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
551
// The model should now contain "a 1/", "a2/", "a2/b2/", and "a2/b2/c2/".
552
// It's important that only the parents of "a1/b1/c1" are expanded.
553
QCOMPARE(m_model->count(), 4);
554
QVERIFY(!m_model->isExpanded(0));
555
QVERIFY(m_model->isExpanded(1));
556
QVERIFY(m_model->isExpanded(2));
557
QVERIFY(!m_model->isExpanded(3));
559
// Expand the parents of "a 1/b1".
560
m_model->expandParentItems(KUrl(m_testDir->name() + "a 1/b1"));
561
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(loadingCompleted()), DefaultTimeout));
563
// The model should now contain "a 1/", "a 1/b1/", "a2/", "a2/b2", and "a2/b2/c2/".
564
// It's important that only the parents of "a 1/b1/" and "a2/b2/c2/" are expanded.
565
QCOMPARE(m_model->count(), 5);
566
QVERIFY(m_model->isExpanded(0));
567
QVERIFY(!m_model->isExpanded(1));
568
QVERIFY(m_model->isExpanded(2));
569
QVERIFY(m_model->isExpanded(3));
570
QVERIFY(!m_model->isExpanded(4));
573
void KFileItemModelTest::testSorting()
575
// Create some files with different sizes and modification times to check the different sorting options
576
QDateTime now = QDateTime::currentDateTime();
578
QSet<QByteArray> roles;
579
roles.insert("name");
580
roles.insert("isExpanded");
581
roles.insert("isExpandable");
582
roles.insert("expandedParentsCount");
583
m_model->setRoles(roles);
585
m_testDir->createDir("c/c-2");
586
m_testDir->createFile("c/c-2/c-3");
587
m_testDir->createFile("c/c-1");
589
m_testDir->createFile("a", "A file", now.addDays(-3));
590
m_testDir->createFile("b", "A larger file", now.addDays(0));
591
m_testDir->createDir("c", now.addDays(-2));
592
m_testDir->createFile("d", "The largest file in this directory", now.addDays(-1));
593
m_testDir->createFile("e", "An even larger file", now.addDays(-4));
594
m_testDir->createFile(".f");
596
m_dirLister->openUrl(m_testDir->url());
597
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
599
int index = m_model->index(KUrl(m_testDir->url().url() + "c"));
600
m_model->setExpanded(index, true);
601
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
603
index = m_model->index(KUrl(m_testDir->url().url() + "c/c-2"));
604
m_model->setExpanded(index, true);
605
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
607
// Default: Sort by Name, ascending
608
QCOMPARE(m_model->sortRole(), QByteArray("name"));
609
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
610
QVERIFY(m_model->sortFoldersFirst());
611
QVERIFY(!m_model->showHiddenFiles());
612
QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "d" << "e");
614
QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
616
// Sort by Name, ascending, 'Sort Folders First' disabled
617
m_model->setSortFoldersFirst(false);
618
QCOMPARE(m_model->sortRole(), QByteArray("name"));
619
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
620
QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
621
QCOMPARE(spyItemsMoved.count(), 1);
622
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7);
624
// Sort by Name, descending
625
m_model->setSortFoldersFirst(true);
626
m_model->setSortOrder(Qt::DescendingOrder);
627
QCOMPARE(m_model->sortRole(), QByteArray("name"));
628
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
629
QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a");
630
QCOMPARE(spyItemsMoved.count(), 2);
631
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 6 << 7);
632
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4);
634
// Sort by Date, descending
635
m_model->setSortFoldersFirst(true);
636
m_model->setSortRole("date");
637
QCOMPARE(m_model->sortRole(), QByteArray("date"));
638
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
639
QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "b" << "d" << "a" << "e");
640
QCOMPARE(spyItemsMoved.count(), 1);
641
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 5 << 4 << 6);
643
// Sort by Date, ascending
644
m_model->setSortOrder(Qt::AscendingOrder);
645
QCOMPARE(m_model->sortRole(), QByteArray("date"));
646
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
647
QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "a" << "d" << "b");
648
QCOMPARE(spyItemsMoved.count(), 1);
649
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 1 << 2 << 3 << 7 << 6 << 5 << 4);
651
// Sort by Date, ascending, 'Sort Folders First' disabled
652
m_model->setSortFoldersFirst(false);
653
QCOMPARE(m_model->sortRole(), QByteArray("date"));
654
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
655
QVERIFY(!m_model->sortFoldersFirst());
656
QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "c-1" << "c-2" << "c-3" << "d" << "b");
657
QCOMPARE(spyItemsMoved.count(), 1);
658
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7);
660
// Sort by Name, ascending, 'Sort Folders First' disabled
661
m_model->setSortRole("name");
662
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
663
QVERIFY(!m_model->sortFoldersFirst());
664
QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
665
QCOMPARE(spyItemsMoved.count(), 1);
666
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1);
668
// Sort by Size, ascending, 'Sort Folders First' disabled
669
m_model->setSortRole("size");
670
QCOMPARE(m_model->sortRole(), QByteArray("size"));
671
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
672
QVERIFY(!m_model->sortFoldersFirst());
673
QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d");
674
QCOMPARE(spyItemsMoved.count(), 1);
675
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6);
677
QSKIP("2 tests of testSorting() are temporary deactivated as in KFileItemModel resortAllItems() "
678
"always emits a itemsMoved() signal. Before adjusting the tests think about probably introducing "
679
"another signal", SkipSingle);
680
// Internal note: Check comment in KFileItemModel::resortAllItems() for details.
682
// In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model
683
m_model->setSortFoldersFirst(true);
684
QCOMPARE(m_model->sortRole(), QByteArray("size"));
685
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
686
QVERIFY(m_model->sortFoldersFirst());
687
QCOMPARE(itemsInModel(), QStringList() << "c" << "a" << "b" << "e" << "d");
688
QCOMPARE(spyItemsMoved.count(), 0);
690
// Sort by Size, descending, 'Sort Folders First' enabled
691
m_model->setSortOrder(Qt::DescendingOrder);
692
QCOMPARE(m_model->sortRole(), QByteArray("size"));
693
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
694
QVERIFY(m_model->sortFoldersFirst());
695
QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "e" << "b" << "a");
696
QCOMPARE(spyItemsMoved.count(), 1);
697
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 0 << 4 << 3 << 2 << 1);
699
// TODO: Sort by other roles; show/hide hidden files
702
void KFileItemModelTest::testIndexForKeyboardSearch()
705
files << "a" << "aa" << "Image.jpg" << "Image.png" << "Text" << "Text1" << "Text2" << "Text11";
706
m_testDir->createFiles(files);
708
m_dirLister->openUrl(m_testDir->url());
709
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
711
// Search from index 0
712
QCOMPARE(m_model->indexForKeyboardSearch("a", 0), 0);
713
QCOMPARE(m_model->indexForKeyboardSearch("aa", 0), 1);
714
QCOMPARE(m_model->indexForKeyboardSearch("i", 0), 2);
715
QCOMPARE(m_model->indexForKeyboardSearch("image", 0), 2);
716
QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 0), 2);
717
QCOMPARE(m_model->indexForKeyboardSearch("image.png", 0), 3);
718
QCOMPARE(m_model->indexForKeyboardSearch("t", 0), 4);
719
QCOMPARE(m_model->indexForKeyboardSearch("text", 0), 4);
720
QCOMPARE(m_model->indexForKeyboardSearch("text1", 0), 5);
721
QCOMPARE(m_model->indexForKeyboardSearch("text2", 0), 6);
722
QCOMPARE(m_model->indexForKeyboardSearch("text11", 0), 7);
724
// Start a search somewhere in the middle
725
QCOMPARE(m_model->indexForKeyboardSearch("a", 1), 1);
726
QCOMPARE(m_model->indexForKeyboardSearch("i", 3), 3);
727
QCOMPARE(m_model->indexForKeyboardSearch("t", 5), 5);
728
QCOMPARE(m_model->indexForKeyboardSearch("text1", 6), 7);
730
// Test searches that go past the last item back to index 0
731
QCOMPARE(m_model->indexForKeyboardSearch("a", 2), 0);
732
QCOMPARE(m_model->indexForKeyboardSearch("i", 7), 2);
733
QCOMPARE(m_model->indexForKeyboardSearch("image.jpg", 3), 2);
734
QCOMPARE(m_model->indexForKeyboardSearch("text2", 7), 6);
736
// Test searches that yield no result
737
QCOMPARE(m_model->indexForKeyboardSearch("aaa", 0), -1);
738
QCOMPARE(m_model->indexForKeyboardSearch("b", 0), -1);
739
QCOMPARE(m_model->indexForKeyboardSearch("image.svg", 0), -1);
740
QCOMPARE(m_model->indexForKeyboardSearch("text3", 0), -1);
741
QCOMPARE(m_model->indexForKeyboardSearch("text3", 5), -1);
743
// Test upper case searches (note that search is case insensitive)
744
QCOMPARE(m_model->indexForKeyboardSearch("A", 0), 0);
745
QCOMPARE(m_model->indexForKeyboardSearch("aA", 0), 1);
746
QCOMPARE(m_model->indexForKeyboardSearch("TexT", 5), 5);
747
QCOMPARE(m_model->indexForKeyboardSearch("IMAGE", 4), 2);
749
// TODO: Maybe we should also test keyboard searches in directories which are not sorted by Name?
752
void KFileItemModelTest::testNameFilter()
755
files << "A1" << "A2" << "Abc" << "Bcd" << "Cde";
756
m_testDir->createFiles(files);
758
m_dirLister->openUrl(m_testDir->url());
759
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
761
m_model->setNameFilter("A"); // Shows A1, A2 and Abc
762
QCOMPARE(m_model->count(), 3);
764
m_model->setNameFilter("A2"); // Shows only A2
765
QCOMPARE(m_model->count(), 1);
767
m_model->setNameFilter("A2"); // Shows only A1
768
QCOMPARE(m_model->count(), 1);
770
m_model->setNameFilter("Bc"); // Shows "Abc" and "Bcd"
771
QCOMPARE(m_model->count(), 2);
773
m_model->setNameFilter("bC"); // Shows "Abc" and "Bcd"
774
QCOMPARE(m_model->count(), 2);
776
m_model->setNameFilter(QString()); // Shows again all items
777
QCOMPARE(m_model->count(), 5);
780
bool KFileItemModelTest::isModelConsistent() const
782
if (m_model->m_items.count() != m_model->m_itemData.count()) {
786
for (int i = 0; i < m_model->count(); ++i) {
787
const KFileItem item = m_model->fileItem(i);
789
qWarning() << "Item" << i << "is null";
793
const int itemIndex = m_model->index(item);
794
if (itemIndex != i) {
795
qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
803
QStringList KFileItemModelTest::itemsInModel() const
806
for (int i = 0; i < m_model->count(); i++) {
807
items << m_model->data(i).value("name").toString();
812
QTEST_KDEMAIN(KFileItemModelTest, NoGUI)
814
#include "kfileitemmodeltest.moc"