┌   ┐
54
└   ┘

summaryrefslogtreecommitdiff
path: root/src/tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests')
-rw-r--r--src/tests/CMakeLists.txt10
-rw-r--r--src/tests/kfileitemmodelbenchmark.cpp334
-rw-r--r--src/tests/kfileitemmodeltest.cpp180
-rw-r--r--src/tests/kitemlistkeyboardsearchmanagertest.cpp29
4 files changed, 524 insertions, 29 deletions
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 84c2e5ae5..dd761fc90 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -57,6 +57,16 @@ set(kfileitemmodeltest_SRCS
kde4_add_unit_test(kfileitemmodeltest TEST ${kfileitemmodeltest_SRCS})
target_link_libraries(kfileitemmodeltest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+# KFileItemModelBenchmark
+set(kfileitemmodelbenchmark_SRCS
+ kfileitemmodelbenchmark.cpp
+ testdir.cpp
+ ../kitemviews/kfileitemmodel.cpp
+ ../kitemviews/kitemmodelbase.cpp
+)
+kde4_add_executable(kfileitemmodelbenchmark TEST ${kfileitemmodelbenchmark_SRCS})
+target_link_libraries(kfileitemmodelbenchmark dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+
# KItemListKeyboardSearchManagerTest
set(kitemlistkeyboardsearchmanagertest_SRCS
kitemlistkeyboardsearchmanagertest.cpp
diff --git a/src/tests/kfileitemmodelbenchmark.cpp b/src/tests/kfileitemmodelbenchmark.cpp
new file mode 100644
index 000000000..f72e43ede
--- /dev/null
+++ b/src/tests/kfileitemmodelbenchmark.cpp
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * Copyright (C) 2011 by Peter Penz <[email protected]> *
+ * Copyright (C) 2013 by Frank Reininghaus <[email protected]> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
+ ***************************************************************************/
+
+#include <qtest_kde.h>
+
+#include "kitemviews/kfileitemmodel.h"
+#include "kitemviews/private/kfileitemmodelsortalgorithm.h"
+
+#include "testdir.h"
+
+#include <KRandomSequence>
+
+void myMessageOutput(QtMsgType type, const char* msg)
+{
+ switch (type) {
+ case QtDebugMsg:
+ break;
+ case QtWarningMsg:
+ break;
+ case QtCriticalMsg:
+ fprintf(stderr, "Critical: %s\n", msg);
+ break;
+ case QtFatalMsg:
+ fprintf(stderr, "Fatal: %s\n", msg);
+ abort();
+ default:
+ break;
+ }
+}
+
+namespace {
+ const int DefaultTimeout = 5000;
+};
+
+Q_DECLARE_METATYPE(KFileItemList)
+Q_DECLARE_METATYPE(KItemRangeList)
+
+class KFileItemModelBenchmark : public QObject
+{
+ Q_OBJECT
+
+public:
+ KFileItemModelBenchmark();
+
+private slots:
+ void insertAndRemoveManyItems_data();
+ void insertAndRemoveManyItems();
+ void insertManyChildItems();
+
+private:
+ static KFileItemList createFileItemList(const QStringList& fileNames, const QString& urlPrefix = QLatin1String("file:///"));
+};
+
+KFileItemModelBenchmark::KFileItemModelBenchmark()
+{
+}
+
+void KFileItemModelBenchmark::insertAndRemoveManyItems_data()
+{
+ QTest::addColumn<KFileItemList>("initialItems");
+ QTest::addColumn<KFileItemList>("newItems");
+ QTest::addColumn<KFileItemList>("removedItems");
+ QTest::addColumn<KFileItemList>("expectedFinalItems");
+ QTest::addColumn<KItemRangeList>("expectedItemsInserted");
+ QTest::addColumn<KItemRangeList>("expectedItemsRemoved");
+
+ QList<int> sizes;
+ sizes << 1000 << 4000 << 16000 << 64000 << 256000;
+ //sizes << 50000 << 100000 << 150000 << 200000 << 250000;
+
+ foreach (int n, sizes) {
+ QStringList allStrings;
+ for (int i = 0; i < n; ++i) {
+ allStrings << QString::number(i);
+ }
+
+ // We want to keep the sorting overhead in the benchmark low.
+ // Therefore, we do not use natural sorting. However, this
+ // means that our list is currently not sorted.
+ allStrings.sort();
+
+ KFileItemList all = createFileItemList(allStrings);
+
+ KFileItemList firstHalf, secondHalf, even, odd;
+ for (int i = 0; i < n; ++i) {
+ if (i < n / 2) {
+ firstHalf << all.at(i);
+ } else {
+ secondHalf << all.at(i);
+ }
+
+ if (i % 2 == 0) {
+ even << all.at(i);
+ } else {
+ odd << all.at(i);
+ }
+ }
+
+ KItemRangeList itemRangeListFirstHalf;
+ itemRangeListFirstHalf << KItemRange(0, firstHalf.count());
+
+ KItemRangeList itemRangeListSecondHalf;
+ itemRangeListSecondHalf << KItemRange(firstHalf.count(), secondHalf.count());
+
+ KItemRangeList itemRangeListOddInserted, itemRangeListOddRemoved;
+ for (int i = 0; i < odd.count(); ++i) {
+ // Note that the index in the KItemRange is the index of
+ // the model *before* the items have been inserted.
+ itemRangeListOddInserted << KItemRange(i + 1, 1);
+ itemRangeListOddRemoved << KItemRange(2 * i + 1, 1);
+ }
+
+ const int bufferSize = 128;
+ char buffer[bufferSize];
+
+ snprintf(buffer, bufferSize, "all--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << KFileItemList() << all << KItemRangeList() << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "1st half + 2nd half--n=%i", n);
+ QTest::newRow(buffer) << firstHalf << secondHalf << KFileItemList() << all << itemRangeListSecondHalf << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "2nd half + 1st half--n=%i", n);
+ QTest::newRow(buffer) << secondHalf << firstHalf << KFileItemList() << all << itemRangeListFirstHalf << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "even + odd--n=%i", n);
+ QTest::newRow(buffer) << even << odd << KFileItemList() << all << itemRangeListOddInserted << KItemRangeList();
+
+ snprintf(buffer, bufferSize, "all - 2nd half--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << secondHalf << firstHalf << KItemRangeList() << itemRangeListSecondHalf;
+
+ snprintf(buffer, bufferSize, "all - 1st half--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << firstHalf << secondHalf << KItemRangeList() << itemRangeListFirstHalf;
+
+ snprintf(buffer, bufferSize, "all - odd--n=%i", n);
+ QTest::newRow(buffer) << all << KFileItemList() << odd << even << KItemRangeList() << itemRangeListOddRemoved;
+ }
+}
+
+void KFileItemModelBenchmark::insertAndRemoveManyItems()
+{
+ QFETCH(KFileItemList, initialItems);
+ QFETCH(KFileItemList, newItems);
+ QFETCH(KFileItemList, removedItems);
+ QFETCH(KFileItemList, expectedFinalItems);
+ QFETCH(KItemRangeList, expectedItemsInserted);
+ QFETCH(KItemRangeList, expectedItemsRemoved);
+
+ KFileItemModel model;
+
+ // Avoid overhead caused by natural sorting
+ // and determining the isDir/isLink roles.
+ model.m_naturalSorting = false;
+ model.setRoles(QSet<QByteArray>() << "text");
+
+ QSignalSpy spyItemsInserted(&model, SIGNAL(itemsInserted(KItemRangeList)));
+ QSignalSpy spyItemsRemoved(&model, SIGNAL(itemsRemoved(KItemRangeList)));
+
+ QBENCHMARK {
+ model.slotClear();
+ model.slotItemsAdded(model.directory(), initialItems);
+ model.slotCompleted();
+ QCOMPARE(model.count(), initialItems.count());
+
+ if (!newItems.isEmpty()) {
+ model.slotItemsAdded(model.directory(), newItems);
+ model.slotCompleted();
+ }
+ QCOMPARE(model.count(), initialItems.count() + newItems.count());
+
+ if (!removedItems.isEmpty()) {
+ model.removeItems(removedItems, KFileItemModel::DeleteItemData);
+ }
+ QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
+ }
+
+ QVERIFY(model.isConsistent());
+
+ for (int i = 0; i < model.count(); ++i) {
+ QCOMPARE(model.fileItem(i), expectedFinalItems.at(i));
+ }
+
+ if (!expectedItemsInserted.empty()) {
+ QVERIFY(!spyItemsInserted.empty());
+ const KItemRangeList actualItemsInserted = spyItemsInserted.last().first().value<KItemRangeList>();
+ QCOMPARE(actualItemsInserted, expectedItemsInserted);
+ }
+
+ if (!expectedItemsRemoved.empty()) {
+ QVERIFY(!spyItemsRemoved.empty());
+ const KItemRangeList actualItemsRemoved = spyItemsRemoved.last().first().value<KItemRangeList>();
+ QCOMPARE(actualItemsRemoved, expectedItemsRemoved);
+ }
+}
+
+void KFileItemModelBenchmark::insertManyChildItems()
+{
+ // TODO: this function needs to be adjusted to the changes in KFileItemModel
+ // (replacement of slotNewItems(KFileItemList) by slotItemsAdded(KUrl,KFileItemList))
+ // Currently, this function tries to insert child items of multiple
+ // directories by invoking the slot only once.
+#if 0
+ qInstallMsgHandler(myMessageOutput);
+
+ KFileItemModel model;
+
+ // Avoid overhead caused by natural sorting.
+ model.m_naturalSorting = false;
+
+ QSet<QByteArray> modelRoles = model.roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ model.setRoles(modelRoles);
+ model.setSortDirectoriesFirst(false);
+
+ // Create a test folder with a 3-level tree structure of folders.
+ TestDir testFolder;
+ int numberOfFolders = 0;
+
+ QStringList subFolderNames;
+ subFolderNames << "a/" << "b/" << "c/" << "d/";
+
+ foreach (const QString& s1, subFolderNames) {
+ ++numberOfFolders;
+ foreach (const QString& s2, subFolderNames) {
+ ++numberOfFolders;
+ foreach (const QString& s3, subFolderNames) {
+ testFolder.createDir("level-1-" + s1 + "level-2-" + s2 + "level-3-" + s3);
+ ++numberOfFolders;
+ }
+ }
+ }
+
+ // Open the folder in the model and expand all subfolders.
+ model.loadDirectory(testFolder.url());
+ QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ int index = 0;
+ while (index < model.count()) {
+ if (model.isExpandable(index)) {
+ model.setExpanded(index, true);
+
+ if (!model.data(index).value("text").toString().startsWith("level-3")) {
+ // New subfolders will appear unless we are on the final level already.
+ QVERIFY(QTest::kWaitForSignal(&model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ }
+
+ QVERIFY(model.isExpanded(index));
+ }
+ ++index;
+ }
+
+ QCOMPARE(model.count(), numberOfFolders);
+
+ // Create a list of many file items, which will be added to each of the
+ // "level 1", "level 2", and "level 3" folders.
+ const int filesPerDirectory = 500;
+ QStringList allStrings;
+ for (int i = 0; i < filesPerDirectory; ++i) {
+ allStrings << QString::number(i);
+ }
+ allStrings.sort();
+
+ KFileItemList newItems;
+
+ // Also keep track of all expected items, including the existing
+ // folders, to verify the final state of the model.
+ KFileItemList allExpectedItems;
+
+ for (int i = 0; i < model.count(); ++i) {
+ const KFileItem folderItem = model.fileItem(i);
+ allExpectedItems << folderItem;
+
+ const KUrl folderUrl = folderItem.url();
+ KFileItemList itemsInFolder = createFileItemList(allStrings, folderUrl.url(KUrl::AddTrailingSlash));
+
+ newItems.append(itemsInFolder);
+ allExpectedItems.append(itemsInFolder);
+ }
+
+ // Bring the items into random order.
+ KRandomSequence randomSequence(0);
+ randomSequence.randomize(newItems);
+
+ // Measure how long it takes to insert and then remove all files.
+ QBENCHMARK {
+ model.slotNewItems(newItems);
+ model.slotCompleted();
+
+ QCOMPARE(model.count(), allExpectedItems.count());
+ QVERIFY(model.isConsistent());
+ for (int i = 0; i < model.count(); ++i) {
+ QCOMPARE(model.fileItem(i), allExpectedItems.at(i));
+ }
+
+ model.slotItemsDeleted(newItems);
+ QCOMPARE(model.count(), numberOfFolders);
+ QVERIFY(model.isConsistent());
+ }
+#endif
+}
+
+KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList& fileNames, const QString& prefix)
+{
+ // Suppress 'file does not exist anymore' messages from KFileItemPrivate::init().
+ qInstallMsgHandler(myMessageOutput);
+
+ KFileItemList result;
+ foreach (const QString& name, fileNames) {
+ const KUrl url(prefix + name);
+ const KFileItem item(url, QString(), KFileItem::Unknown);
+ result << item;
+ }
+ return result;
+}
+
+QTEST_KDEMAIN(KFileItemModelBenchmark, NoGUI)
+
+#include "kfileitemmodelbenchmark.moc"
diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp
index fd6c2be90..0ad7a378d 100644
--- a/src/tests/kfileitemmodeltest.cpp
+++ b/src/tests/kfileitemmodeltest.cpp
@@ -21,6 +21,8 @@
#include <qtest_kde.h>
#include <KDirLister>
+#include <kio/job.h>
+
#include "kitemviews/kfileitemmodel.h"
#include "kitemviews/private/kfileitemmodeldirlister.h"
#include "testdir.h"
@@ -72,6 +74,7 @@ private slots:
void testItemRangeConsistencyWhenInsertingItems();
void testExpandItems();
void testExpandParentItems();
+ void testMakeExpandedItemHidden();
void testSorting();
void testIndexForKeyboardSearch();
void testNameFilter();
@@ -80,9 +83,9 @@ private slots:
void testRemoveHiddenItems();
void collapseParentOfHiddenItems();
void removeParentOfHiddenItems();
+ void testGeneralParentChildRelationships();
private:
- bool isModelConsistent() const;
QStringList itemsInModel() const;
private:
@@ -157,7 +160,7 @@ void KFileItemModelTest::testNewItems()
QCOMPARE(m_model->count(), 3);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testRemoveItems()
@@ -167,13 +170,13 @@ void KFileItemModelTest::testRemoveItems()
m_model->loadDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 2);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
m_testDir->removeFile("a.txt");
m_model->m_dirLister->updateDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 1);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testDirLoadingCompleted()
@@ -216,7 +219,7 @@ void KFileItemModelTest::testDirLoadingCompleted()
QCOMPARE(itemsRemovedSpy.count(), 2);
QCOMPARE(m_model->count(), 4);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testSetData()
@@ -237,7 +240,7 @@ void KFileItemModelTest::testSetData()
values = m_model->data(0);
QCOMPARE(values.value("customRole1").toString(), QString("Test1"));
QCOMPARE(values.value("customRole2").toString(), QString("Test2"));
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testSetDataWithModifiedSortRole_data()
@@ -319,7 +322,7 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
QCOMPARE(m_model->data(0).value("rating").toInt(), ratingIndex0);
QCOMPARE(m_model->data(1).value("rating").toInt(), ratingIndex1);
QCOMPARE(m_model->data(2).value("rating").toInt(), ratingIndex2);
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
void KFileItemModelTest::testChangeSortRole()
@@ -397,7 +400,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
}
- QVERIFY(isModelConsistent());
+ QVERIFY(m_model->isConsistent());
}
QCOMPARE(m_model->count(), 201);
@@ -531,6 +534,7 @@ void KFileItemModelTest::testExpandItems()
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 4)); // 4 items removed
+ QVERIFY(m_model->isConsistent());
// Clear the model, reload the folder and try to restore the expanded folders.
m_model->clear();
@@ -547,6 +551,7 @@ void KFileItemModelTest::testExpandItems()
QVERIFY(m_model->isExpanded(3));
QVERIFY(!m_model->isExpanded(4));
QCOMPARE(m_model->expandedDirectories(), allFolders);
+ QVERIFY(m_model->isConsistent());
// Move to a sub folder, then call restoreExpandedFolders() *before* going back.
// This is how DolphinView restores the expanded folders when navigating in history.
@@ -609,6 +614,56 @@ void KFileItemModelTest::testExpandParentItems()
QVERIFY(m_model->isExpanded(2));
QVERIFY(m_model->isExpanded(3));
QVERIFY(!m_model->isExpanded(4));
+ QVERIFY(m_model->isConsistent());
+}
+
+/**
+ * Renaming an expanded folder by prepending its name with a dot makes it
+ * hidden. Verify that this does not cause an inconsistent model state and
+ * a crash later on, see https://bugs.kde.org/show_bug.cgi?id=311947
+ */
+void KFileItemModelTest::testMakeExpandedItemHidden()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ m_testDir->createFile("1a/2a/3a");
+ m_testDir->createFile("1a/2a/3b");
+ m_testDir->createFile("1a/2b");
+ m_testDir->createFile("1b");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ // So far, the model contains only "1a/" and "1b".
+ QCOMPARE(m_model->count(), 2);
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+ // Now "1a/2a" and "1a/2b" have appeared.
+ QCOMPARE(m_model->count(), 4);
+ m_model->setExpanded(1, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 6);
+
+ // Rename "1a/2" and make it hidden.
+ const QString oldPath = m_model->fileItem(0).url().path() + "/2a";
+ const QString newPath = m_model->fileItem(0).url().path() + "/.2a";
+
+ KIO::SimpleJob* job = KIO::rename(oldPath, newPath, KIO::HideProgressInfo);
+ bool ok = job->exec();
+ QVERIFY(ok);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
+
+ // "1a/2" and its subfolders have disappeared now.
+ QVERIFY(m_model->isConsistent());
+ QCOMPARE(m_model->count(), 3);
+
+ m_model->setExpanded(0, false);
+ QCOMPARE(m_model->count(), 2);
+
}
void KFileItemModelTest::testSorting()
@@ -834,12 +889,12 @@ void KFileItemModelTest::testEmptyPath()
const KUrl emptyUrl;
QVERIFY(emptyUrl.path().isEmpty());
-
+
const KUrl url("file:///test/");
-
+
KFileItemList items;
items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown);
- m_model->slotNewItems(items);
+ m_model->slotItemsAdded(emptyUrl, items);
m_model->slotCompleted();
}
@@ -1025,27 +1080,96 @@ void KFileItemModelTest::removeParentOfHiddenItems()
QCOMPARE(itemsInModel(), QStringList() << "a" << "1");
}
-bool KFileItemModelTest::isModelConsistent() const
+/**
+ * Create a tree structure where parent-child relationships can not be
+ * determined by parsing the URLs, and verify that KFileItemModel
+ * handles them correctly.
+ */
+void KFileItemModelTest::testGeneralParentChildRelationships()
{
- if (m_model->m_items.count() != m_model->m_itemData.count()) {
- return false;
- }
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
- for (int i = 0; i < m_model->count(); ++i) {
- const KFileItem item = m_model->fileItem(i);
- if (item.isNull()) {
- qWarning() << "Item" << i << "is null";
- return false;
- }
+ QStringList files;
+ files << "parent1/realChild1/realGrandChild1" << "parent2/realChild2/realGrandChild2";
+ m_testDir->createFiles(files);
- const int itemIndex = m_model->index(item);
- if (itemIndex != i) {
- qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
- return false;
- }
- }
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2");
+
+ // Expand all folders.
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2");
+
+ m_model->setExpanded(1, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2");
+
+ m_model->setExpanded(3, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2");
+
+ m_model->setExpanded(4, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2" << "realGrandChild2");
+
+ // Add some more children and grand-children.
+ const KUrl parent1 = m_model->fileItem(0).url();
+ const KUrl parent2 = m_model->fileItem(3).url();
+ const KUrl realChild1 = m_model->fileItem(1).url();
+ const KUrl realChild2 = m_model->fileItem(4).url();
+
+ m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(KUrl("child1"), QString(), KFileItem::Unknown));
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2");
+
+ m_model->slotItemsAdded(parent2, KFileItemList() << KFileItem(KUrl("child2"), QString(), KFileItem::Unknown));
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
- return true;
+ m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(KUrl("grandChild1"), QString(), KFileItem::Unknown));
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
+
+ m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(KUrl("grandChild1"), QString(), KFileItem::Unknown));
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
+
+ m_model->slotItemsAdded(realChild2, KFileItemList() << KFileItem(KUrl("grandChild2"), QString(), KFileItem::Unknown));
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "grandChild2" << "realGrandChild2" << "child2");
+
+ // Set a name filter that matches nothing -> only expanded folders remain.
+ QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+ m_model->setNameFilter("xyz");
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2" << "realChild2");
+ QCOMPARE(itemsRemovedSpy.count(), 1);
+ QList<QVariant> arguments = itemsRemovedSpy.takeFirst();
+ KItemRangeList itemRangeList = arguments.at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 3) << KItemRange(7, 3));
+
+ // Collapse "parent1".
+ m_model->setExpanded(0, false);
+ QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2" << "realChild2");
+ QCOMPARE(itemsRemovedSpy.count(), 1);
+ arguments = itemsRemovedSpy.takeFirst();
+ itemRangeList = arguments.at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 1));
+
+ // Remove "parent2".
+ m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1));
+ QCOMPARE(itemsInModel(), QStringList() << "parent1");
+ QCOMPARE(itemsRemovedSpy.count(), 1);
+ arguments = itemsRemovedSpy.takeFirst();
+ itemRangeList = arguments.at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2));
+
+ // Clear filter, verify that no items reappear.
+ m_model->setNameFilter(QString());
+ QCOMPARE(itemsInModel(), QStringList() << "parent1");
}
QStringList KFileItemModelTest::itemsInModel() const
diff --git a/src/tests/kitemlistkeyboardsearchmanagertest.cpp b/src/tests/kitemlistkeyboardsearchmanagertest.cpp
index cf15324e2..7d5fc3b9a 100644
--- a/src/tests/kitemlistkeyboardsearchmanagertest.cpp
+++ b/src/tests/kitemlistkeyboardsearchmanagertest.cpp
@@ -31,6 +31,7 @@ private slots:
void testBasicKeyboardSearch();
void testAbortedKeyboardSearch();
void testRepeatedKeyPress();
+ void testPressShift();
private:
KItemListKeyboardSearchManager m_keyboardSearchManager;
@@ -39,7 +40,7 @@ private:
void KItemListKeyboardSearchManagerTest::init()
{
// Make sure that the previous search string is cleared
- m_keyboardSearchManager.addKeys("");
+ m_keyboardSearchManager.cancelSearch();
}
void KItemListKeyboardSearchManagerTest::testBasicKeyboardSearch()
@@ -120,6 +121,32 @@ void KItemListKeyboardSearchManagerTest::testRepeatedKeyPress()
QCOMPARE(spy.takeFirst(), QList<QVariant>() << "pppq" << false);
}
+void KItemListKeyboardSearchManagerTest::testPressShift()
+{
+ // If the user presses Shift, i.e., to get a character like '_',
+ // KItemListController calls the addKeys(QString) method with an empty
+ // string. Make sure that this does not reset the current search. See
+ // https://bugs.kde.org/show_bug.cgi?id=321286
+
+ QSignalSpy spy(&m_keyboardSearchManager, SIGNAL(changeCurrentItem(QString,bool)));
+
+ // Simulate that the user enters "a_b".
+ m_keyboardSearchManager.addKeys("a");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "a" << true);
+
+ m_keyboardSearchManager.addKeys("");
+ QCOMPARE(spy.count(), 0);
+
+ m_keyboardSearchManager.addKeys("_");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "a_" << false);
+
+ m_keyboardSearchManager.addKeys("b");
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.takeFirst(), QList<QVariant>() << "a_b" << false);
+}
+
QTEST_KDEMAIN(KItemListKeyboardSearchManagerTest, NoGUI)
#include "kitemlistkeyboardsearchmanagertest.moc"