~ubuntu-branches/debian/sid/kde-baseapps/sid

« back to all changes in this revision

Viewing changes to dolphin/src/tests/kfileitemmodeltest.cpp

  • Committer: Package Import Robot
  • Author(s): Modestas Vainius, Eshat Cakar, Pino Toscano
  • Date: 2012-06-09 22:18:08 UTC
  • mfrom: (4.2.1) (6.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20120609221808-h1l0ekd5qmb8nefr
Tags: 4:4.8.4-1
* New upstream release.

[ Eshat Cakar ]
* Add watch file.
* Bump kde-sc-dev-latest build dependency to version 4:4.8.4.
* Update installed files.

[ Pino Toscano ]
* Move files of the konqueror documentation from kde-baseapps-data to
  konqueror itself.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
 
3
 *   Copyright (C) 2011 by Frank Reininghaus <frank78ac@googlemail.com>    *
 
4
 *                                                                         *
 
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.                                   *
 
9
 *                                                                         *
 
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.                          *
 
14
 *                                                                         *
 
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
 ***************************************************************************/
 
20
 
 
21
#include <qtest_kde.h>
 
22
 
 
23
#include <KDirLister>
 
24
#include "kitemviews/kfileitemmodel.h"
 
25
#include "testdir.h"
 
26
 
 
27
void myMessageOutput(QtMsgType type, const char* msg)
 
28
{
 
29
    switch (type) {
 
30
     case QtDebugMsg:
 
31
         break;
 
32
     case QtWarningMsg:
 
33
         break;
 
34
     case QtCriticalMsg:
 
35
         fprintf(stderr, "Critical: %s\n", msg);
 
36
         break;
 
37
     case QtFatalMsg:
 
38
         fprintf(stderr, "Fatal: %s\n", msg);
 
39
         abort();
 
40
     default:
 
41
        break;
 
42
    }
 
43
 }
 
44
 
 
45
namespace {
 
46
    const int DefaultTimeout = 5000;
 
47
};
 
48
 
 
49
Q_DECLARE_METATYPE(KItemRangeList)
 
50
Q_DECLARE_METATYPE(QList<int>)
 
51
 
 
52
class KFileItemModelTest : public QObject
 
53
{
 
54
    Q_OBJECT
 
55
 
 
56
private slots:
 
57
    void init();
 
58
    void cleanup();
 
59
 
 
60
    void testDefaultRoles();
 
61
    void testDefaultSortRole();
 
62
    void testDefaultGroupedSorting();
 
63
    void testNewItems();
 
64
    void testRemoveItems();
 
65
    void testLoadingCompleted();
 
66
    void testSetData();
 
67
    void testSetDataWithModifiedSortRole_data();
 
68
    void testSetDataWithModifiedSortRole();
 
69
    void testModelConsistencyWhenInsertingItems();
 
70
    void testItemRangeConsistencyWhenInsertingItems();
 
71
    void testExpandItems();
 
72
    void testExpandParentItems();
 
73
    void testSorting();
 
74
 
 
75
    void testIndexForKeyboardSearch();
 
76
 
 
77
    void testNameFilter();
 
78
 
 
79
private:
 
80
    bool isModelConsistent() const;
 
81
    QStringList itemsInModel() const;
 
82
 
 
83
private:
 
84
    KFileItemModel* m_model;
 
85
    KDirLister* m_dirLister;
 
86
    TestDir* m_testDir;
 
87
};
 
88
 
 
89
void KFileItemModelTest::init()
 
90
{
 
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);
 
94
 
 
95
    qRegisterMetaType<KItemRange>("KItemRange");
 
96
    qRegisterMetaType<KItemRangeList>("KItemRangeList");
 
97
    qRegisterMetaType<KFileItemList>("KFileItemList");
 
98
 
 
99
    m_testDir = new TestDir();
 
100
    m_dirLister = new KDirLister();
 
101
    m_dirLister->setAutoUpdate(false);
 
102
    m_model = new KFileItemModel(m_dirLister);
 
103
}
 
104
 
 
105
void KFileItemModelTest::cleanup()
 
106
{
 
107
    delete m_model;
 
108
    m_model = 0;
 
109
 
 
110
    delete m_dirLister;
 
111
    m_dirLister = 0;
 
112
 
 
113
    delete m_testDir;
 
114
    m_testDir = 0;
 
115
}
 
116
 
 
117
void KFileItemModelTest::testDefaultRoles()
 
118
{
 
119
    const QSet<QByteArray> roles = m_model->roles();
 
120
    QCOMPARE(roles.count(), 2);
 
121
    QVERIFY(roles.contains("name"));
 
122
    QVERIFY(roles.contains("isDir"));
 
123
}
 
124
 
 
125
void KFileItemModelTest::testDefaultSortRole()
 
126
{
 
127
    QCOMPARE(m_model->sortRole(), QByteArray("name"));
 
128
 
 
129
    QStringList files;
 
130
    files << "c.txt" << "a.txt" << "b.txt";
 
131
 
 
132
    m_testDir->createFiles(files);
 
133
 
 
134
    m_dirLister->openUrl(m_testDir->url());
 
135
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
136
 
 
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"));
 
141
}
 
142
 
 
143
void KFileItemModelTest::testDefaultGroupedSorting()
 
144
{
 
145
    QCOMPARE(m_model->groupedSorting(), false);
 
146
}
 
147
 
 
148
void KFileItemModelTest::testNewItems()
 
149
{
 
150
    QStringList files;
 
151
    files << "a.txt" << "b.txt" << "c.txt";
 
152
    m_testDir->createFiles(files);
 
153
 
 
154
    m_dirLister->openUrl(m_testDir->url());
 
155
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
156
 
 
157
    QCOMPARE(m_model->count(), 3);
 
158
 
 
159
    QVERIFY(isModelConsistent());
 
160
}
 
161
 
 
162
void KFileItemModelTest::testRemoveItems()
 
163
{
 
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());
 
170
 
 
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());
 
176
}
 
177
 
 
178
void KFileItemModelTest::testLoadingCompleted()
 
179
{
 
180
    QSignalSpy loadingCompletedSpy(m_model, SIGNAL(loadingCompleted()));
 
181
    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
 
182
    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
 
183
 
 
184
    m_testDir->createFiles(QStringList() << "a.txt" << "b.txt" << "c.txt");
 
185
 
 
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);
 
192
 
 
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);
 
200
 
 
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);
 
209
 
 
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);
 
217
 
 
218
    QVERIFY(isModelConsistent());
 
219
}
 
220
 
 
221
void KFileItemModelTest::testSetData()
 
222
{
 
223
    m_testDir->createFile("a.txt");
 
224
 
 
225
    m_dirLister->openUrl(m_testDir->url());
 
226
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
227
 
 
228
    QHash<QByteArray, QVariant> values;
 
229
    values.insert("customRole1", "Test1");
 
230
    values.insert("customRole2", "Test2");
 
231
 
 
232
    QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)));
 
233
    m_model->setData(0, values);
 
234
    QCOMPARE(itemsChangedSpy.count(), 1);
 
235
 
 
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());
 
240
}
 
241
 
 
242
void KFileItemModelTest::testSetDataWithModifiedSortRole_data()
 
243
{
 
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");
 
250
 
 
251
    // Default setup:
 
252
    // Index 0 = rating 2
 
253
    // Index 1 = rating 4
 
254
    // Index 2 = rating 6
 
255
 
 
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;
 
259
 
 
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;
 
263
}
 
264
 
 
265
void KFileItemModelTest::testSetDataWithModifiedSortRole()
 
266
{
 
267
    QFETCH(int, changedIndex);
 
268
    QFETCH(int, changedRating);
 
269
    QFETCH(bool, expectMoveSignal);
 
270
    QFETCH(int, ratingIndex0);
 
271
    QFETCH(int, ratingIndex1);
 
272
    QFETCH(int, ratingIndex2);
 
273
 
 
274
    // Changing the value of a sort-role must result in
 
275
    // a reordering of the items.
 
276
    QCOMPARE(m_model->sortRole(), QByteArray("name"));
 
277
 
 
278
    QStringList files;
 
279
    files << "a.txt" << "b.txt" << "c.txt";
 
280
    m_testDir->createFiles(files);
 
281
 
 
282
    m_dirLister->openUrl(m_testDir->url());
 
283
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
284
 
 
285
    // Fill the "rating" role of each file:
 
286
    // a.txt -> 2
 
287
    // b.txt -> 4
 
288
    // c.txt -> 6
 
289
 
 
290
    QHash<QByteArray, QVariant> ratingA;
 
291
    ratingA.insert("rating", 2);
 
292
    m_model->setData(0, ratingA);
 
293
 
 
294
    QHash<QByteArray, QVariant> ratingB;
 
295
    ratingB.insert("rating", 4);
 
296
    m_model->setData(1, ratingB);
 
297
 
 
298
    QHash<QByteArray, QVariant> ratingC;
 
299
    ratingC.insert("rating", 6);
 
300
    m_model->setData(2, ratingC);
 
301
 
 
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);
 
306
 
 
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);
 
312
 
 
313
    if (expectMoveSignal) {
 
314
        QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
 
315
    }
 
316
 
 
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());
 
321
}
 
322
 
 
323
void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
 
324
{
 
325
    //QSKIP("Temporary disabled", SkipSingle);
 
326
 
 
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);
 
337
 
 
338
    // Insert 10 items for 20 times. After each insert operation the model consistency
 
339
    // is checked.
 
340
    QSet<int> insertedItems;
 
341
    for (int i = 0; i < 20; ++i) {
 
342
        QSignalSpy spy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
 
343
 
 
344
        for (int j = 0; j < 10; ++j) {
 
345
            int itemName = qrand();
 
346
            while (insertedItems.contains(itemName)) {
 
347
                itemName = qrand();
 
348
            }
 
349
            insertedItems.insert(itemName);
 
350
 
 
351
            m_testDir->createFile(QString::number(itemName));
 
352
        }
 
353
 
 
354
        m_dirLister->updateDirectory(m_testDir->url());
 
355
        if (spy.count() == 0) {
 
356
            QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
357
        }
 
358
 
 
359
        QVERIFY(isModelConsistent());
 
360
    }
 
361
 
 
362
    QCOMPARE(m_model->count(), 201);
 
363
}
 
364
 
 
365
void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
 
366
{
 
367
    QStringList files;
 
368
    files << "B" << "E" << "G";
 
369
    m_testDir->createFiles(files);
 
370
 
 
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));
 
376
 
 
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));
 
381
 
 
382
    // The indexes of the item-ranges must always be related to the model before
 
383
    // the items have been inserted. Having:
 
384
    //   0 1 2
 
385
    //   B E G
 
386
    // and inserting A, C, D, F the resulting model will be:
 
387
    //   0 1 2 3 4 5 6
 
388
    //   A B C D E F G
 
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
 
393
 
 
394
    files.clear();
 
395
    files << "A" << "C" << "D" << "F";
 
396
    m_testDir->createFiles(files);
 
397
 
 
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));
 
401
 
 
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));
 
406
}
 
407
 
 
408
void KFileItemModelTest::testExpandItems()
 
409
{
 
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);
 
418
 
 
419
    QStringList files;
 
420
    files << "a/a/1" << "a/a-1/1"; // missing folders are created automatically
 
421
    m_testDir->createFiles(files);
 
422
 
 
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");
 
426
 
 
427
    m_dirLister->openUrl(m_testDir->url());
 
428
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
429
 
 
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());
 
435
 
 
436
    QSignalSpy spyInserted(m_model, SIGNAL(itemsInserted(KItemRangeList)));
 
437
 
 
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"));
 
444
 
 
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
 
448
 
 
449
    QVERIFY(m_model->isExpandable(1));
 
450
    QVERIFY(!m_model->isExpanded(1));
 
451
    QVERIFY(m_model->isExpandable(2));
 
452
    QVERIFY(!m_model->isExpanded(2));
 
453
 
 
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"));
 
460
 
 
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
 
464
 
 
465
    QVERIFY(!m_model->isExpandable(2));
 
466
    QVERIFY(!m_model->isExpanded(2));
 
467
 
 
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);
 
474
 
 
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
 
478
 
 
479
    QVERIFY(!m_model->isExpandable(4));
 
480
    QVERIFY(!m_model->isExpanded(4));
 
481
 
 
482
    QSignalSpy spyRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
 
483
 
 
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
 
489
 
 
490
    QCOMPARE(spyRemoved.count(), 1);
 
491
    itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
 
492
    QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
 
493
 
 
494
    // Clear the model, reload the folder and try to restore the expanded folders.
 
495
    m_model->clear();
 
496
    QCOMPARE(m_model->count(), 0);
 
497
    QVERIFY(m_model->expandedUrls().empty());
 
498
 
 
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);
 
509
 
 
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);
 
520
}
 
521
 
 
522
void KFileItemModelTest::testExpandParentItems()
 
523
{
 
524
    // Create a tree structure of folders:
 
525
    // a 1/
 
526
    // a 1/b1/
 
527
    // a 1/b1/c1/
 
528
    // a2/
 
529
    // a2/b2/
 
530
    // a2/b2/c2/
 
531
    // a2/b2/c2/d2/
 
532
    QSet<QByteArray> modelRoles = m_model->roles();
 
533
    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
 
534
    m_model->setRoles(modelRoles);
 
535
 
 
536
    QStringList files;
 
537
    files << "a 1/b1/c1/file.txt" << "a2/b2/c2/d2/file.txt"; // missing folders are created automatically
 
538
    m_testDir->createFiles(files);
 
539
 
 
540
    m_dirLister->openUrl(m_testDir->url());
 
541
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
542
 
 
543
    // So far, the model contains only "a 1/" and "a2/".
 
544
    QCOMPARE(m_model->count(), 2);
 
545
    QVERIFY(m_model->expandedUrls().empty());
 
546
 
 
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));
 
550
 
 
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));
 
558
 
 
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));
 
562
 
 
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));
 
571
}
 
572
 
 
573
void KFileItemModelTest::testSorting()
 
574
{
 
575
    // Create some files with different sizes and modification times to check the different sorting options
 
576
    QDateTime now = QDateTime::currentDateTime();
 
577
 
 
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);
 
584
 
 
585
    m_testDir->createDir("c/c-2");
 
586
    m_testDir->createFile("c/c-2/c-3");
 
587
    m_testDir->createFile("c/c-1");
 
588
 
 
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");
 
595
 
 
596
    m_dirLister->openUrl(m_testDir->url());
 
597
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
598
 
 
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));
 
602
 
 
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));
 
606
 
 
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");
 
613
 
 
614
    QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
 
615
 
 
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);
 
623
 
 
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);
 
633
 
 
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);
 
642
 
 
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);
 
650
 
 
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);
 
659
 
 
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);
 
667
 
 
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);
 
676
 
 
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.
 
681
 
 
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);
 
689
 
 
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);
 
698
 
 
699
    // TODO: Sort by other roles; show/hide hidden files
 
700
}
 
701
 
 
702
void KFileItemModelTest::testIndexForKeyboardSearch()
 
703
{
 
704
    QStringList files;
 
705
    files << "a" << "aa" << "Image.jpg" << "Image.png" << "Text" << "Text1" << "Text2" << "Text11";
 
706
    m_testDir->createFiles(files);
 
707
 
 
708
    m_dirLister->openUrl(m_testDir->url());
 
709
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
710
 
 
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);
 
723
 
 
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);
 
729
 
 
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);
 
735
 
 
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);
 
742
 
 
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);
 
748
 
 
749
    // TODO: Maybe we should also test keyboard searches in directories which are not sorted by Name?
 
750
}
 
751
 
 
752
void KFileItemModelTest::testNameFilter()
 
753
{
 
754
    QStringList files;
 
755
    files << "A1" << "A2" << "Abc" << "Bcd" << "Cde";
 
756
    m_testDir->createFiles(files);
 
757
 
 
758
    m_dirLister->openUrl(m_testDir->url());
 
759
    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
 
760
 
 
761
    m_model->setNameFilter("A"); // Shows A1, A2 and Abc
 
762
    QCOMPARE(m_model->count(), 3);
 
763
 
 
764
    m_model->setNameFilter("A2"); // Shows only A2
 
765
    QCOMPARE(m_model->count(), 1);
 
766
 
 
767
    m_model->setNameFilter("A2"); // Shows only A1
 
768
    QCOMPARE(m_model->count(), 1);
 
769
 
 
770
    m_model->setNameFilter("Bc"); // Shows "Abc" and "Bcd"
 
771
    QCOMPARE(m_model->count(), 2);
 
772
 
 
773
    m_model->setNameFilter("bC"); // Shows "Abc" and "Bcd"
 
774
    QCOMPARE(m_model->count(), 2);
 
775
 
 
776
    m_model->setNameFilter(QString()); // Shows again all items
 
777
    QCOMPARE(m_model->count(), 5);
 
778
}
 
779
 
 
780
bool KFileItemModelTest::isModelConsistent() const
 
781
{
 
782
    if (m_model->m_items.count() != m_model->m_itemData.count()) {
 
783
        return false;
 
784
    }
 
785
 
 
786
    for (int i = 0; i < m_model->count(); ++i) {
 
787
        const KFileItem item = m_model->fileItem(i);
 
788
        if (item.isNull()) {
 
789
            qWarning() << "Item" << i << "is null";
 
790
            return false;
 
791
        }
 
792
 
 
793
        const int itemIndex = m_model->index(item);
 
794
        if (itemIndex != i) {
 
795
            qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
 
796
            return false;
 
797
        }
 
798
    }
 
799
 
 
800
    return true;
 
801
}
 
802
 
 
803
QStringList KFileItemModelTest::itemsInModel() const
 
804
{
 
805
    QStringList items;
 
806
    for (int i = 0; i < m_model->count(); i++) {
 
807
        items << m_model->data(i).value("name").toString();
 
808
    }
 
809
    return items;
 
810
}
 
811
 
 
812
QTEST_KDEMAIN(KFileItemModelTest, NoGUI)
 
813
 
 
814
#include "kfileitemmodeltest.moc"