-/***************************************************************************
- * Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
- * Copyright (C) 2011 by Frank Reininghaus <frank78ac@googlemail.com> *
- * *
- * 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 *
- ***************************************************************************/
+/*
+ * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
+ * SPDX-FileCopyrightText: 2011 Frank Reininghaus <frank78ac@googlemail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
-#include <QTest>
+#include <QMimeData>
+#include <QRandomGenerator>
#include <QSignalSpy>
+#include <QStandardPaths>
+#include <QTest>
#include <QTimer>
-#include <QMimeData>
-#include <kio/job.h>
+#include <KDirLister>
+#include <KIO/SimpleJob>
#include "kitemviews/kfileitemmodel.h"
-#include "kitemviews/private/kfileitemmodeldirlister.h"
#include "testdir.h"
-void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
- Q_UNUSED(context);
+ Q_UNUSED(context)
switch (type) {
case QtDebugMsg:
fprintf(stderr, "Fatal: %s\n", msg.toLocal8Bit().data());
abort();
default:
- break;
+ break;
}
}
{
Q_OBJECT
-private slots:
+private Q_SLOTS:
void init();
+ void initTestCase();
void cleanup();
void testDefaultRoles();
void testMakeExpandedItemHidden();
void testRemoveFilteredExpandedItems();
void testSorting();
+ void testNaturalSorting();
void testIndexForKeyboardSearch();
void testNameFilter();
void testEmptyPath();
void testRefreshExpandedItem();
+ void testAddItemToFilteredExpandedFolder();
+ void testDeleteItemsWithExpandedFolderWithFilter();
+ void testRefreshItemsWithFilter();
+ void testRefreshExpandedFolderWithFilter();
void testRemoveHiddenItems();
void collapseParentOfHiddenItems();
void removeParentOfHiddenItems();
void testCollapseFolderWhileLoading();
void testCreateMimeData();
void testDeleteFileMoreThanOnce();
+ void testInsertAfterExpand();
+ void testCurrentDirRemoved();
+ void testSizeSortingAfterRefresh();
private:
QStringList itemsInModel() const;
private:
- KFileItemModel* m_model;
- TestDir* m_testDir;
+ KFileItemModel *m_model;
+ TestDir *m_testDir;
};
+void KFileItemModelTest::initTestCase()
+{
+ QStandardPaths::setTestModeEnabled(true);
+}
+
void KFileItemModelTest::init()
{
// The item-model tests result in a huge number of debugging
QVERIFY(itemsInsertedSpy.wait());
QCOMPARE(m_model->count(), 3);
- QCOMPARE(m_model->data(0).value("text").toString(), QString("a.txt"));
- QCOMPARE(m_model->data(1).value("text").toString(), QString("b.txt"));
- QCOMPARE(m_model->data(2).value("text").toString(), QString("c.txt"));
+ QCOMPARE(m_model->data(0).value("text").toString(), QStringLiteral("a.txt"));
+ QCOMPARE(m_model->data(1).value("text").toString(), QStringLiteral("b.txt"));
+ QCOMPARE(m_model->data(2).value("text").toString(), QStringLiteral("c.txt"));
}
void KFileItemModelTest::testDefaultGroupedSorting()
QCOMPARE(itemsChangedSpy.count(), 1);
values = m_model->data(0);
- QCOMPARE(values.value("customRole1").toString(), QString("Test1"));
- QCOMPARE(values.value("customRole2").toString(), QString("Test2"));
+ QCOMPARE(values.value("customRole1").toString(), QStringLiteral("Test1"));
+ QCOMPARE(values.value("customRole2").toString(), QStringLiteral("Test2"));
QVERIFY(m_model->isConsistent());
}
// Index 2 = rating 6
QTest::newRow("Index 0: Rating 3") << 0 << 3 << false << 3 << 4 << 6;
- QTest::newRow("Index 0: Rating 5") << 0 << 5 << true << 4 << 5 << 6;
- QTest::newRow("Index 0: Rating 8") << 0 << 8 << true << 4 << 6 << 8;
+ QTest::newRow("Index 0: Rating 5") << 0 << 5 << true << 4 << 5 << 6;
+ QTest::newRow("Index 0: Rating 8") << 0 << 8 << true << 4 << 6 << 8;
- QTest::newRow("Index 2: Rating 1") << 2 << 1 << true << 1 << 2 << 4;
- QTest::newRow("Index 2: Rating 3") << 2 << 3 << true << 2 << 3 << 4;
+ QTest::newRow("Index 2: Rating 1") << 2 << 1 << true << 1 << 2 << 4;
+ QTest::newRow("Index 2: Rating 3") << 2 << 3 << true << 2 << 3 << 4;
QTest::newRow("Index 2: Rating 5") << 2 << 5 << false << 2 << 4 << 5;
}
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.jpg" << "c.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.jpg"
+ << "c.txt");
// Simulate that KFileItemModelRolesUpdater determines the mime type.
// Resorting the files by 'type' will only work immediately if their
// The actual order of the files might depend on the translation of the
// result of KFileItem::mimeComment() in the user's language.
QStringList version1;
- version1 << "b.jpg" << "a.txt" << "c.txt";
+ version1 << "b.jpg"
+ << "a.txt"
+ << "c.txt";
QStringList version2;
- version2 << "a.txt" << "c.txt" << "b.jpg";
+ version2 << "a.txt"
+ << "c.txt"
+ << "b.jpg";
const bool ok1 = (itemsInModel() == version1);
const bool ok2 = (itemsInModel() == version2);
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt");
// We rename a.txt to d.txt. Even though the size has not changed at all,
// the model must re-sort the items.
m_model->setData(0, data);
QVERIFY(itemsMovedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "b.txt"
+ << "c.txt"
+ << "d.txt");
// We rename d.txt back to a.txt using the dir lister's refreshItems() signal.
const KFileItem fileItemD = m_model->fileItem(2);
m_model->slotRefreshItems({qMakePair(fileItemD, fileItemA)});
QVERIFY(itemsMovedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt");
}
void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
itemsInsertedSpy.clear();
for (int j = 0; j < 10; ++j) {
- int itemName = qrand();
+ int itemName = QRandomGenerator::global()->generate();
while (insertedItems.contains(itemName)) {
- itemName = qrand();
+ itemName = QRandomGenerator::global()->generate();
}
insertedItems.insert(itemName);
// Test expanding subfolders in a folder with the items "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1".
// Besides testing the basic item expansion functionality, the test makes sure that
// KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b)
- // yields the correct result for "a/a/1" and "a/a-1/", whis is non-trivial because they share the
+ // yields the correct result for "a/a/1" and "a/a-1/", which is non-trivial because they share the
// first three characters.
QSet<QByteArray> originalModelRoles = m_model->roles();
QSet<QByteArray> modelRoles = originalModelRoles;
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/a/1", "a/a-1/1"});
// Store the URLs of all folders in a set.
QSet<QUrl> allFolders;
- allFolders << QUrl::fromLocalFile(m_testDir->path() + "/a")
- << QUrl::fromLocalFile(m_testDir->path() + "/a/a")
+ allFolders << QUrl::fromLocalFile(m_testDir->path() + "/a") << QUrl::fromLocalFile(m_testDir->path() + "/a/a")
<< QUrl::fromLocalFile(m_testDir->path() + "/a/a-1");
m_model->loadDirectory(m_testDir->url());
m_model->setExpanded(1, true);
QVERIFY(m_model->isExpanded(1));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
- QCOMPARE(m_model->expandedDirectories(), QSet<QUrl>() << QUrl::fromLocalFile(m_testDir->path() + "/a")
- << QUrl::fromLocalFile(m_testDir->path() + "/a/a"));
+ QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
+ QCOMPARE(m_model->expandedDirectories(), QSet<QUrl>() << QUrl::fromLocalFile(m_testDir->path() + "/a") << QUrl::fromLocalFile(m_testDir->path() + "/a/a"));
QCOMPARE(itemsInsertedSpy.count(), 1);
itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
m_model->setExpanded(3, true);
QVERIFY(m_model->isExpanded(3));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
QCOMPARE(m_model->expandedDirectories(), allFolders);
QCOMPARE(itemsInsertedSpy.count(), 1);
m_model->loadDirectory(m_testDir->url());
m_model->restoreExpandedDirectories(allFolders);
QVERIFY(loadingCompletedSpy.wait());
- QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
QVERIFY(m_model->isExpanded(0));
QVERIFY(m_model->isExpanded(1));
QVERIFY(!m_model->isExpanded(2));
// This is how DolphinView restores the expanded folders when navigating in history.
m_model->loadDirectory(QUrl::fromLocalFile(m_testDir->path() + "/a/a/"));
QVERIFY(loadingCompletedSpy.wait());
- QCOMPARE(m_model->count(), 1); // 1 item: "1"
+ QCOMPARE(m_model->count(), 1); // 1 item: "1"
m_model->restoreExpandedDirectories(allFolders);
m_model->loadDirectory(m_testDir->url());
QVERIFY(loadingCompletedSpy.wait());
- QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/a/", "a/a/1", "a/a-1/", "a/a-1/1"
QCOMPARE(m_model->expandedDirectories(), allFolders);
// Remove all expanded items by changing the roles
// a2/b2/c2/
// a2/b2/c2/d2/
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a 1/b1/c1/file.txt", "a2/b2/c2/d2/file.txt"});
QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"1a/2a/3a", "1a/2a/3b", "1a/2b", "1b"});
const QUrl oldUrl = QUrl::fromLocalFile(m_model->fileItem(0).url().path() + "/2a");
const QUrl newUrl = QUrl::fromLocalFile(m_model->fileItem(0).url().path() + "/.2a");
- KIO::SimpleJob* job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
+ KIO::SimpleJob *job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
bool ok = job->exec();
QVERIFY(ok);
QVERIFY(itemsRemovedSpy.wait());
m_model->setExpanded(0, false);
QCOMPARE(m_model->count(), 2);
-
}
void KFileItemModelTest::testRemoveFilteredExpandedItems()
QSet<QByteArray> originalModelRoles = m_model->roles();
QSet<QByteArray> modelRoles = originalModelRoles;
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"folder/child", "file"});
QVERIFY(!m_model->isExpandable(1));
QVERIFY(!m_model->isExpanded(0));
QVERIFY(!m_model->isExpanded(1));
- QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "folder"
+ << "file");
// Expand "folder" -> "folder/child" becomes visible.
m_model->setExpanded(0, true);
QVERIFY(m_model->isExpanded(0));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "folder" << "child" << "file");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "folder"
+ << "child"
+ << "file");
// Add a name filter.
m_model->setNameFilter("f");
- QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "folder"
+ << "file");
m_model->setNameFilter("fo");
QCOMPARE(itemsInModel(), QStringList() << "folder");
// Remove the name filter and verify that "folder/child" does not reappear.
m_model->setNameFilter(QString());
- QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "folder"
+ << "file");
+}
+
+void KFileItemModelTest::testSizeSortingAfterRefresh()
+{
+ // testDir structure is as follows
+ // ./
+ // ├─ a
+ // ├─ b
+ // ├─ c/
+ // │ ├─ c-2/
+ // │ │ ├─ c-3
+ // │ ├─ c-1
+ // ├─ d
+ // ├─ e
+
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
+ QVERIFY(itemsMovedSpy.isValid());
+
+ // Create some files with different sizes and modification times to check the different sorting options
+ QDateTime now = QDateTime::currentDateTime();
+
+ QSet<QByteArray> roles;
+ roles.insert("text");
+ roles.insert("isExpanded");
+ roles.insert("isExpandable");
+ roles.insert("expandedParentsCount");
+ m_model->setRoles(roles);
+
+ m_testDir->createDir("c/c-2");
+ m_testDir->createFile("c/c-2/c-3");
+ m_testDir->createFile("c/c-1");
+
+ m_testDir->createFile("a", "A file", now.addDays(-3));
+ m_testDir->createFile("b", "A larger file", now.addDays(0));
+ m_testDir->createDir("c", now.addDays(-2));
+ m_testDir->createFile("d", "The largest file in this directory", now.addDays(-1));
+ m_testDir->createFile("e", "An even larger file", now.addDays(-4));
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ int index = m_model->index(QUrl(m_testDir->url().url() + "/c"));
+ m_model->setExpanded(index, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ index = m_model->index(QUrl(m_testDir->url().url() + "/c/c-2"));
+ m_model->setExpanded(index, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Default: Sort by Name, ascending
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
+ QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "d"
+ << "e");
+
+ // Sort by Size, ascending, before refresh
+ m_model->setSortRole("size");
+ QCOMPARE(m_model->sortRole(), QByteArray("size"));
+ QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "e"
+ << "d");
+
+ // Refresh directory
+ m_model->refreshDirectory(m_model->directory());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Expand folders again
+ index = m_model->index(QUrl(m_testDir->url().url() + "/c"));
+ m_model->setExpanded(index, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ index = m_model->index(QUrl(m_testDir->url().url() + "/c/c-2"));
+ m_model->setExpanded(index, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Sort by Size, ascending, after refresh
+ QCOMPARE(m_model->sortRole(), QByteArray("size"));
+ QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "e"
+ << "d");
}
void KFileItemModelTest::testSorting()
{
+ // testDir structure is as follows
+ // ./
+ // ├─ .g/
+ // ├─ a
+ // ├─ b
+ // ├─ c/
+ // │ ├─ c-2/
+ // │ │ ├─ c-3
+ // │ ├─ c-1
+ // ├─ .f
+ // ├─ d
+ // ├─ e
+
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
QVERIFY(itemsMovedSpy.isValid());
m_testDir->createFile("d", "The largest file in this directory", now.addDays(-1));
m_testDir->createFile("e", "An even larger file", now.addDays(-4));
m_testDir->createFile(".f");
+ m_testDir->createDir(".g");
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 5));
int index = m_model->index(QUrl(m_testDir->url().url() + "/c"));
m_model->setExpanded(index, true);
QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2));
index = m_model->index(QUrl(m_testDir->url().url() + "/c/c-2"));
m_model->setExpanded(index, true);
QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 1));
// Default: Sort by Name, ascending
QCOMPARE(m_model->sortRole(), QByteArray("text"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(m_model->sortDirectoriesFirst());
QVERIFY(!m_model->showHiddenFiles());
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "d" << "e");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "d"
+ << "e");
// Sort by Name, ascending, 'Sort Folders First' disabled
m_model->setSortDirectoriesFirst(false);
QCOMPARE(m_model->sortRole(), QByteArray("text"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c"
+ << "c-1"
+ << "c-2"
+ << "c-3"
+ << "d"
+ << "e");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 6));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
// Sort by Name, descending
m_model->setSortDirectoriesFirst(true);
m_model->setSortOrder(Qt::DescendingOrder);
QCOMPARE(m_model->sortRole(), QByteArray("text"));
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "d" << "b" << "a");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "e"
+ << "d"
+ << "b"
+ << "a");
QCOMPARE(itemsMovedSpy.count(), 2);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 6));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 4));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 6 << 5 << 4);
// Sort by Date, descending
m_model->setSortDirectoriesFirst(true);
m_model->setSortRole("modificationtime");
QCOMPARE(m_model->sortRole(), QByteArray("modificationtime"));
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "b" << "d" << "a" << "e");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "b"
+ << "d"
+ << "a"
+ << "e");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 4));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 5 << 4 << 6);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 5 << 4 << 6);
// Sort by Date, ascending
m_model->setSortOrder(Qt::AscendingOrder);
QCOMPARE(m_model->sortRole(), QByteArray("modificationtime"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "e" << "a" << "d" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "e"
+ << "a"
+ << "d"
+ << "b");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 4));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 6 << 5 << 4);
// Sort by Date, ascending, 'Sort Folders First' disabled
m_model->setSortDirectoriesFirst(false);
QCOMPARE(m_model->sortRole(), QByteArray("modificationtime"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(!m_model->sortDirectoriesFirst());
- QCOMPARE(itemsInModel(), QStringList() << "e" << "a" << "c" << "c-1" << "c-2" << "c-3" << "d" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "e"
+ << "a"
+ << "c"
+ << "c-1"
+ << "c-2"
+ << "c-3"
+ << "d"
+ << "b");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 6));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1);
// Sort by Name, ascending, 'Sort Folders First' disabled
m_model->setSortRole("text");
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(!m_model->sortDirectoriesFirst());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c" << "c-1" << "c-2" << "c-3" << "d" << "e");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c"
+ << "c-1"
+ << "c-2"
+ << "c-3"
+ << "d"
+ << "e");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 8));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1);
// Sort by Size, ascending, 'Sort Folders First' disabled
m_model->setSortRole("size");
QCOMPARE(m_model->sortRole(), QByteArray("size"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(!m_model->sortDirectoriesFirst());
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "e"
+ << "d");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 8));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6);
// In 'Sort by Size' mode, folders are always first -> changing 'Sort Folders First' does not resort the model
m_model->setSortDirectoriesFirst(true);
QCOMPARE(m_model->sortRole(), QByteArray("size"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(m_model->sortDirectoriesFirst());
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "a" << "b" << "e" << "d");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "e"
+ << "d");
QCOMPARE(itemsMovedSpy.count(), 0);
// Sort by Size, descending, 'Sort Folders First' enabled
QCOMPARE(m_model->sortRole(), QByteArray("size"));
QCOMPARE(m_model->sortOrder(), Qt::DescendingOrder);
QVERIFY(m_model->sortDirectoriesFirst());
- QCOMPARE(itemsInModel(), QStringList() << "c" << "c-2" << "c-3" << "c-1" << "d" << "e" << "b" << "a");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "d"
+ << "e"
+ << "b"
+ << "a");
QCOMPARE(itemsMovedSpy.count(), 1);
QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 4));
- QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int> >(), QList<int>() << 7 << 6 << 5 << 4);
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 6 << 5 << 4);
- // TODO: Sort by other roles; show/hide hidden files
+ // 'Show Hidden Files' enabled
+ m_model->setShowHiddenFiles(true);
+ QVERIFY(m_model->showHiddenFiles());
+ QVERIFY(!m_model->sortHiddenLast());
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << ".g"
+ << "d"
+ << "e"
+ << "b"
+ << "a"
+ << ".f");
+ QCOMPARE(itemsMovedSpy.count(), 0);
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ QCOMPARE(itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>(), KItemRangeList() << KItemRange(4, 1) << KItemRange(8, 1));
+
+ // 'Sort Hidden Files Last' enabled
+ m_model->setSortHiddenLast(true);
+ QVERIFY(m_model->sortHiddenLast());
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "d"
+ << "e"
+ << "b"
+ << "a"
+ << ".g"
+ << ".f");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsInsertedSpy.count(), 0);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 5));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 8 << 4 << 5 << 6 << 7);
+
+ // Sort by Name
+ m_model->setSortRole("text");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "e"
+ << "d"
+ << "b"
+ << "a"
+ << ".g"
+ << ".f");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 2));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 5 << 4);
+
+ // Sort ascending
+ m_model->setSortOrder(Qt::AscendingOrder);
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "c-2"
+ << "c-3"
+ << "c-1"
+ << "a"
+ << "b"
+ << "d"
+ << "e"
+ << ".g"
+ << ".f");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(4, 4));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 7 << 6 << 5 << 4);
+
+ // 'Sort Folders First' disabled
+ m_model->setSortDirectoriesFirst(false);
+ QVERIFY(!m_model->sortDirectoriesFirst());
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c"
+ << "c-1"
+ << "c-2"
+ << "c-3"
+ << "d"
+ << "e"
+ << ".f"
+ << ".g");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(0, 10));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7 << 9 << 8);
+}
+
+void KFileItemModelTest::testNaturalSorting()
+{
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
+ QVERIFY(itemsMovedSpy.isValid());
+
+ m_model->setSortRole("text");
+ m_model->setShowHiddenFiles(true);
+
+ m_testDir->createFiles({".a", "a.txt", "b.txt", "a 1.txt", "a 2.txt", "a 10.txt", "a.tar", "b.tar"});
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Sort by Name, ascending, natural sorting enabled
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
+ QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << "a.tar"
+ << "a.txt"
+ << "a 1.txt"
+ << "a 2.txt"
+ << "a 10.txt"
+ << "b.tar"
+ << "b.txt");
+
+ // Sort by Extension
+ m_model->setSortRole("extension");
+ QCOMPARE(m_model->sortRole(), QByteArray("extension"));
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << "a.tar"
+ << "b.tar"
+ << "a.txt"
+ << "a 1.txt"
+ << "a 2.txt"
+ << "a 10.txt"
+ << "b.txt");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(2, 5));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 3 << 4 << 5 << 6 << 2);
+
+ // Disable natural sorting, refresh directory for the change to take effect
+ m_model->m_naturalSorting = false;
+ m_model->refreshDirectory(m_model->directory());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Sort by Extension, ascending, natural sorting disabled
+ QCOMPARE(m_model->sortRole(), QByteArray("extension"));
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << "a.tar"
+ << "b.tar"
+ << "a 1.txt"
+ << "a 10.txt"
+ << "a 2.txt"
+ << "a.txt"
+ << "b.txt");
+
+ // Sort by Name
+ m_model->setSortRole("text");
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << "a 1.txt"
+ << "a 10.txt"
+ << "a 2.txt"
+ << "a.tar"
+ << "a.txt"
+ << "b.tar"
+ << "b.txt");
+ QCOMPARE(itemsMovedSpy.count(), 1);
+ QCOMPARE(itemsMovedSpy.first().at(0).value<KItemRange>(), KItemRange(1, 6));
+ QCOMPARE(itemsMovedSpy.takeFirst().at(1).value<QList<int>>(), QList<int>() << 4 << 6 << 1 << 2 << 3 << 5);
}
void KFileItemModelTest::testIndexForKeyboardSearch()
{
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
- m_testDir->createFiles({"a", "aa", "Image.jpg", "Image.png", "Text", "Text1", "Text2", "Text11"});
+ m_testDir->createFiles({"a", "aa", "Image.jpg", "Image.png", "Text", "Text1", "Text2", "Text11", "U", "Ü", "Üu", "Ž"});
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
QCOMPARE(m_model->indexForKeyboardSearch("text1", 0), 5);
QCOMPARE(m_model->indexForKeyboardSearch("text2", 0), 6);
QCOMPARE(m_model->indexForKeyboardSearch("text11", 0), 7);
+ QCOMPARE(m_model->indexForKeyboardSearch("u", 0), 8);
+ QCOMPARE(m_model->indexForKeyboardSearch("üu", 0), 10);
+ QCOMPARE(m_model->indexForKeyboardSearch("ž", 0), 11);
// Start a search somewhere in the middle
QCOMPARE(m_model->indexForKeyboardSearch("a", 1), 1);
QCOMPARE(m_model->indexForKeyboardSearch("TexT", 5), 5);
QCOMPARE(m_model->indexForKeyboardSearch("IMAGE", 4), 2);
+ // Test searches that match items with marks
+ QCOMPARE(m_model->indexForKeyboardSearch("u", 9), 9);
+ QCOMPARE(m_model->indexForKeyboardSearch("u", 10), 10);
+ QCOMPARE(m_model->indexForKeyboardSearch("uu", 0), 10);
+ QCOMPARE(m_model->indexForKeyboardSearch("z", 0), 11);
+
// TODO: Maybe we should also test keyboard searches in directories which are not sorted by Name?
}
QVERIFY(itemsChangedSpy.isValid());
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/1", "a/2", "3", "4"});
QVERIFY(m_model->isExpanded(0));
}
+/**
+ * Verifies that adding an item to an expanded folder that's filtered makes the parental chain visible.
+ */
+void KFileItemModelTest::testAddItemToFilteredExpandedFolder()
+{
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy fileItemsChangedSpy(m_model, &KFileItemModel::fileItemsChanged);
+
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ m_testDir->createFile("a/b/file");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(m_model->count(), 1); // "a
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Expand "a/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/b/file"
+
+ const QUrl urlB = m_model->fileItem(1).url();
+
+ // Set a filter that matches ".txt" extension
+ m_model->setNameFilter("*.txt");
+ QCOMPARE(m_model->count(), 0); // Everything got hidden since we don't have a .txt file yet
+
+ m_model->slotItemsAdded(urlB, KFileItemList() << KFileItem(QUrl("a/b/newItem.txt")));
+ m_model->slotCompleted();
+
+ // Entire parental chain should now be shown
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/b/newItem.txt"
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "newItem.txt");
+
+ // Items should be indented in hierarchy
+ QCOMPARE(m_model->expandedParentsCount(0), 0);
+ QCOMPARE(m_model->expandedParentsCount(1), 1);
+ QCOMPARE(m_model->expandedParentsCount(2), 2);
+}
+
+/**
+ * Verifies that deleting the last filter-passing child from expanded folders
+ * makes the parental chain hidden.
+ */
+void KFileItemModelTest::testDeleteItemsWithExpandedFolderWithFilter()
+{
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
+
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ m_testDir->createFile("a/b/file");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(m_model->count(), 1); // "a
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Expand "a/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/b/file"
+
+ // Set a filter that matches "file" extension
+ m_model->setNameFilter("file");
+ QCOMPARE(m_model->count(), 3); // Everything is still shown
+
+ // Delete "file"
+ QCOMPARE(itemsRemovedSpy.count(), 0);
+ m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(2));
+ QCOMPARE(itemsRemovedSpy.count(), 1);
+
+ // Entire parental chain should now be filtered
+ QCOMPARE(m_model->count(), 0);
+ QCOMPARE(m_model->m_filteredItems.size(), 2);
+}
+
+/**
+ * Verifies that the fileItemsChanged signal is raised with the correct index after renaming files with filter set.
+ * The rename operation will cause one item to be filtered out and another item to be reordered.
+ */
+void KFileItemModelTest::testRefreshItemsWithFilter()
+{
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
+ QSignalSpy itemsChangedSpy(m_model, &KFileItemModel::itemsChanged);
+ QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
+
+ // Creates three .txt files
+ m_testDir->createFiles({"b.txt", "c.txt", "d.txt"});
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ QCOMPARE(m_model->count(), 3); // "b.txt", "c.txt", "d.txt"
+
+ // Set a filter that matches ".txt" extension
+ m_model->setNameFilter("*.txt");
+ QCOMPARE(m_model->count(), 3); // Still all items are shown
+ QCOMPARE(itemsInModel(),
+ QStringList() << "b.txt"
+ << "c.txt"
+ << "d.txt");
+
+ // Objects used to rename
+ const KFileItem fileItemC_txt = m_model->fileItem(1);
+ KFileItem fileItemC_cfg = fileItemC_txt;
+ fileItemC_cfg.setUrl(QUrl("c.cfg"));
+
+ const KFileItem fileItemD_txt = m_model->fileItem(2);
+ KFileItem fileItemA_txt = fileItemD_txt;
+ fileItemA_txt.setUrl(QUrl("a.txt"));
+
+ // Rename "c.txt" to "c.cfg"; and rename "d.txt" to "a.txt"
+ QCOMPARE(itemsRemovedSpy.count(), 0);
+ QCOMPARE(itemsChangedSpy.count(), 0);
+ m_model->slotRefreshItems({qMakePair(fileItemC_txt, fileItemC_cfg), qMakePair(fileItemD_txt, fileItemA_txt)});
+ QCOMPARE(itemsRemovedSpy.count(), 1);
+ QCOMPARE(itemsChangedSpy.count(), 1);
+ QCOMPARE(m_model->count(), 2); // Only "a.txt" and "b.txt". "c.cfg" got filtered out
+
+ QList<QVariant> arguments = itemsChangedSpy.takeLast();
+ KItemRangeList itemRangeList = arguments.at(0).value<KItemRangeList>();
+
+ // We started with the order "b.txt", "c.txt", "d.txt"
+ // "d.txt" started with index "2"
+ // "c.txt" got renamed and got filtered out
+ // "d.txt" index shifted from index "2" to "1"
+ // So we expect index "1" in this argument, meaning "d.txt" was renamed
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 1));
+
+ // Re-sorting is done asynchronously:
+ QCOMPARE(itemsInModel(),
+ QStringList() << "b.txt"
+ << "a.txt"); // Files should still be in the incorrect order
+ QVERIFY(itemsMovedSpy.wait());
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"); // Files were re-sorted and should now be in the correct order
+}
+
+/**
+ * Verifies that parental chains are hidden and shown as needed while their children get filtered/unfiltered due to renaming.
+ * Also verifies that the "isExpanded" and "expandedParentsCount" values are kept for expanded folders that get refreshed.
+ */
+void KFileItemModelTest::testRefreshExpandedFolderWithFilter()
+{
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
+
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ m_testDir->createFile("a/b/someFolder/someFile");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ QCOMPARE(m_model->count(), 1); // Only "a/"
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Expand "a/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // Expand "a/b/someFolder/".
+ m_model->setExpanded(2, true);
+ QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/b/", "a/b/someFolder", "a/b/someFolder/someFile"
+
+ // Set a filter that matches the expanded folder "someFolder"
+ m_model->setNameFilter("someFolder");
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/b/someFolder"
+
+ // Objects used to rename
+ const KFileItem fileItemA = m_model->fileItem(0);
+ KFileItem fileItemARenamed = fileItemA;
+ fileItemARenamed.setUrl(QUrl("a_renamed"));
+
+ const KFileItem fileItemSomeFolder = m_model->fileItem(2);
+ KFileItem fileItemRenamedFolder = fileItemSomeFolder;
+ fileItemRenamedFolder.setUrl(QUrl("/a_renamed/b/renamedFolder"));
+
+ // Rename "a" to "a_renamed"
+ // This way we test if the algorithm is sane as to NOT hide "a_renamed" since it will have visible children
+ m_model->slotRefreshItems({qMakePair(fileItemA, fileItemARenamed)});
+ QCOMPARE(m_model->count(), 3); // Entire parental chain must still be shown
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a_renamed"
+ << "b"
+ << "someFolder");
+
+ // Rename "a_renamed" back to "a"; and "someFolder" to "renamedFolder"
+ m_model->slotRefreshItems({qMakePair(fileItemARenamed, fileItemA), qMakePair(fileItemSomeFolder, fileItemRenamedFolder)});
+ QCOMPARE(m_model->count(), 0); // Entire parental chain became hidden
+
+ // Rename "renamedFolder" back to "someFolder". Filter is passing again
+ m_model->slotRefreshItems({qMakePair(fileItemRenamedFolder, fileItemSomeFolder)});
+ QCOMPARE(m_model->count(), 3); // Entire parental chain is shown again
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "someFolder");
+
+ // slotRefreshItems() should preserve "isExpanded" and "expandedParentsCount" values explicitly in this case
+ QCOMPARE(m_model->m_itemData.at(2)->values.value("isExpanded").toBool(), true);
+ QCOMPARE(m_model->m_itemData.at(2)->values.value("expandedParentsCount"), 2);
+}
+
/**
* Verify that removing hidden files and folders from the model does not
* result in a crash, see https://bugs.kde.org/show_bug.cgi?id=314046
m_model->setShowHiddenFiles(true);
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i");
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << ".b"
+ << "c"
+ << "d"
+ << ".f"
+ << ".g"
+ << "h"
+ << "i");
QCOMPARE(itemsInsertedSpy.count(), 1);
QCOMPARE(itemsRemovedSpy.count(), 0);
KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 8));
m_model->setShowHiddenFiles(false);
- QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "h" << "i");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c"
+ << "d"
+ << "h"
+ << "i");
QCOMPARE(itemsInsertedSpy.count(), 0);
QCOMPARE(itemsRemovedSpy.count(), 1);
itemRangeList = itemsRemovedSpy.takeFirst().at(0).value<KItemRangeList>();
QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 2) << KItemRange(4, 2));
m_model->setShowHiddenFiles(true);
- QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i");
+ QCOMPARE(itemsInModel(),
+ QStringList() << ".a"
+ << ".b"
+ << "c"
+ << "d"
+ << ".f"
+ << ".g"
+ << "h"
+ << "i");
QCOMPARE(itemsInsertedSpy.count(), 1);
QCOMPARE(itemsRemovedSpy.count(), 0);
itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/1", "a/b/1", "a/b/c/1", "a/b/c/d/1"});
QVERIFY(itemsInsertedSpy.wait());
QCOMPARE(m_model->count(), 7); // 7 items: "a/", "a/b/", "a/b/c", "a/b/c/d/", "a/b/c/1", "a/b/1", "a/1"
- // Set a name filter that matches nothing -> only the expanded folders remain.
+ // Set a name filter that matches nothing -> nothing should remain.
m_model->setNameFilter("xyz");
QCOMPARE(itemsRemovedSpy.count(), 1);
- QCOMPARE(m_model->count(), 3);
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c");
+ QCOMPARE(m_model->count(), 0); // Everything is hidden
+ QCOMPARE(itemsInModel(), QStringList());
+
+ // Filter by the file names. Folder "d" will be hidden since it was collapsed
+ m_model->setNameFilter("1");
+ QCOMPARE(itemsRemovedSpy.count(), 1); // nothing was removed, itemsRemovedSpy count will remain the same:
+ QCOMPARE(m_model->count(), 6); // 6 items: "a/", "a/b/", "a/b/c", "a/b/c/1", "a/b/1", "a/1"
// Collapse the folder "a/".
m_model->setExpanded(0, false);
QCOMPARE(m_model->count(), 1);
QCOMPARE(itemsInModel(), QStringList() << "a");
- // Remove the filter -> no files should appear (and we should not get a crash).
+ // Remove the filter -> "a" should still appear (and we should not get a crash).
m_model->setNameFilter(QString());
+ QCOMPARE(itemsRemovedSpy.count(), 2); // nothing was removed, itemsRemovedSpy count will remain the same:
QCOMPARE(m_model->count(), 1);
+ QCOMPARE(itemsInModel(), QStringList() << "a");
}
/**
QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/1", "a/b/1", "a/b/c/1", "a/b/c/d/1"});
QVERIFY(itemsInsertedSpy.wait());
QCOMPARE(m_model->count(), 7); // 7 items: "a/", "a/b/", "a/b/c", "a/b/c/d/", "a/b/c/1", "a/b/1", "a/1"
- // Set a name filter that matches nothing -> only the expanded folders remain.
+ // Set a name filter that matches nothing -> nothing should remain.
m_model->setNameFilter("xyz");
QCOMPARE(itemsRemovedSpy.count(), 1);
+ QCOMPARE(m_model->count(), 0);
+ QCOMPARE(itemsInModel(), QStringList());
+
+ // Filter by "c". Folder "b" will also be shown because it is its parent.
+ m_model->setNameFilter("c");
+ QCOMPARE(itemsRemovedSpy.count(), 1); // nothing was removed, itemsRemovedSpy count will remain the same:
QCOMPARE(m_model->count(), 3);
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c");
// Simulate the deletion of the directory "a/b/".
m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1));
QCOMPARE(itemsRemovedSpy.count(), 2);
- QCOMPARE(m_model->count(), 1);
- QCOMPARE(itemsInModel(), QStringList() << "a");
+ QCOMPARE(m_model->count(), 0); // "a" will be filtered out since it doesn't pass the filter and doesn't have visible children
// Remove the filter -> only the file "a/1" should appear.
m_model->setNameFilter(QString());
QCOMPARE(m_model->count(), 2);
- QCOMPARE(itemsInModel(), QStringList() << "a" << "1");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "1");
}
/**
QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"parent1/realChild1/realGrandChild1", "parent2/realChild2/realGrandChild2"});
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "parent2");
// Expand all folders.
m_model->setExpanded(0, true);
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "parent2");
m_model->setExpanded(1, true);
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "realGrandChild1"
+ << "parent2");
m_model->setExpanded(3, true);
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "realGrandChild1"
+ << "parent2"
+ << "realChild2");
m_model->setExpanded(4, true);
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2" << "realGrandChild2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "realGrandChild1"
+ << "parent2"
+ << "realChild2"
+ << "realGrandChild2");
// Add some more children and grand-children.
const QUrl parent1 = m_model->fileItem(0).url();
m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(QUrl("child1"), QString(), KFileItem::Unknown));
m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "realGrandChild1"
+ << "child1"
+ << "parent2"
+ << "realChild2"
+ << "realGrandChild2");
m_model->slotItemsAdded(parent2, KFileItemList() << KFileItem(QUrl("child2"), QString(), KFileItem::Unknown));
m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
-
- m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(QUrl("grandChild1"), QString(), KFileItem::Unknown));
- m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "realGrandChild1"
+ << "child1"
+ << "parent2"
+ << "realChild2"
+ << "realGrandChild2"
+ << "child2");
m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(QUrl("grandChild1"), QString(), KFileItem::Unknown));
m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "grandChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "grandChild1"
+ << "realGrandChild1"
+ << "child1"
+ << "parent2"
+ << "realChild2"
+ << "realGrandChild2"
+ << "child2");
m_model->slotItemsAdded(realChild2, KFileItemList() << KFileItem(QUrl("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.
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "grandChild1"
+ << "realGrandChild1"
+ << "child1"
+ << "parent2"
+ << "realChild2"
+ << "grandChild2"
+ << "realGrandChild2"
+ << "child2");
+
+ // Set a name filter that matches nothing -> nothing will remain.
m_model->setNameFilter("xyz");
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2" << "realChild2");
+ QCOMPARE(itemsInModel(), QStringList());
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));
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 10));
+
+ // Set a name filter that matches only "realChild". Their prarents should still show.
+ m_model->setNameFilter("realChild");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "realChild1"
+ << "parent2"
+ << "realChild2");
+ QCOMPARE(itemsRemovedSpy.count(), 0); // nothing was removed, itemsRemovedSpy will not be called this time
// Collapse "parent1".
m_model->setExpanded(0, false);
- QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2" << "realChild2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "parent1"
+ << "parent2"
+ << "realChild2");
QCOMPARE(itemsRemovedSpy.count(), 1);
arguments = itemsRemovedSpy.takeFirst();
itemRangeList = arguments.at(0).value<KItemRangeList>();
m_model->setGroupedSorting(true);
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt" << "e.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "b.txt"
+ << "c.txt"
+ << "d.txt"
+ << "e.txt");
- QList<QPair<int, QVariant> > expectedGroups;
+ QList<QPair<int, QVariant>> expectedGroups;
expectedGroups << QPair<int, QVariant>(0, QLatin1String("B"));
expectedGroups << QPair<int, QVariant>(1, QLatin1String("C"));
expectedGroups << QPair<int, QVariant>(2, QLatin1String("D"));
data.insert("text", "a.txt");
m_model->setData(2, data);
QVERIFY(itemsMovedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt"
+ << "e.txt");
expectedGroups.clear();
expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
data.insert("text", "d.txt");
m_model->setData(2, data);
QVERIFY(groupsChangedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.txt" << "e.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "d.txt"
+ << "e.txt");
expectedGroups.clear();
expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
m_model->slotRefreshItems({qMakePair(fileItemD, fileItemC)});
QVERIFY(groupsChangedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt"
+ << "e.txt");
expectedGroups.clear();
expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/b.txt", "a/c.txt", "d/e.txt", "d/f.txt"});
m_model->setGroupedSorting(true);
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "d");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "d");
- QList<QPair<int, QVariant> > expectedGroups;
+ QList<QPair<int, QVariant>> expectedGroups;
expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
expectedGroups << QPair<int, QVariant>(1, QLatin1String("D"));
QCOMPARE(m_model->groups(), expectedGroups);
m_model->setExpanded(0, true);
QVERIFY(m_model->isExpanded(0));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b.txt" << "c.txt" << "d");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b.txt"
+ << "c.txt"
+ << "d");
QCOMPARE(m_model->groups(), expectedGroups);
m_model->setExpanded(3, true);
QVERIFY(m_model->isExpanded(3));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b.txt" << "c.txt" << "d" << "e.txt" << "f.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b.txt"
+ << "c.txt"
+ << "d"
+ << "e.txt"
+ << "f.txt");
QCOMPARE(m_model->groups(), expectedGroups);
}
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFiles({"a/b/c1.txt", "a/b/c2.txt"});
m_model->setExpanded(0, true);
QVERIFY(m_model->isExpanded(0));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b");
m_model->setExpanded(1, true);
QVERIFY(m_model->isExpanded(1));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c1.txt"
+ << "c2.txt");
// Add the files "c1.txt" and "c2.txt" to the model also as top-level items.
// Such a thing can in principle happen when performing a search, and there
items << newItem << m_model->fileItem(2) << m_model->fileItem(3);
m_model->slotItemsAdded(m_model->directory(), items);
m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c1.txt" << "c2.txt" << "a2" << "c1.txt" << "c2.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "b"
+ << "c1.txt"
+ << "c2.txt"
+ << "a2"
+ << "c1.txt"
+ << "c2.txt");
m_model->setExpanded(0, false);
// Test that the right items have been removed, see
// https://bugs.kde.org/show_bug.cgi?id=324371
- QCOMPARE(itemsInModel(), QStringList() << "a" << "a2" << "c1.txt" << "c2.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "a2"
+ << "c1.txt"
+ << "c2.txt");
// Test that resorting does not cause a crash, see
// https://bugs.kde.org/show_bug.cgi?id=325359
// The crash is not 100% reproducible, but Valgrind will report an invalid memory access.
m_model->resortAllItems();
-
}
void KFileItemModelTest::testChangeRolesForFilteredItems()
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "aa.txt" << "aaa.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "aa.txt"
+ << "aaa.txt");
for (int index = 0; index < m_model->count(); ++index) {
// All items should have the "text" and "owner" roles, but not "group".
// Modify the filter, such that "aa.txt" reappears, and verify that all items have the expected roles.
m_model->setNameFilter("aa");
- QCOMPARE(itemsInModel(), QStringList() << "aa.txt" << "aaa.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "aa.txt"
+ << "aaa.txt");
for (int index = 0; index < m_model->count(); ++index) {
// All items should have the "text", "owner", and "group" roles.
// Clear the filter, and verify that all items have the expected roles
m_model->setNameFilter(QString());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "aa.txt" << "aaa.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "aa.txt"
+ << "aaa.txt");
for (int index = 0; index < m_model->count(); ++index) {
// All items should have the "text" and "group" roles, but now "owner".
entry[2].fastInsert(KIO::UDSEntry::UDS_USER, "user-a");
for (int i = 0; i < 3; ++i) {
- entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms.
+ entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms.
entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS, 07777);
entry[i].fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
entry[i].fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0);
m_model->slotItemsAdded(m_testDir->url(), items);
m_model->slotCompleted();
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt");
// Add a filter.
m_model->setNameFilter("a");
// Clear the filter, and verify that the items are sorted correctly.
m_model->setNameFilter(QString());
- QCOMPARE(itemsInModel(), QStringList() << "c.txt" << "a.txt" << "b.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "c.txt"
+ << "a.txt"
+ << "b.txt");
}
void KFileItemModelTest::testRefreshFilteredItems()
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.jpg" << "d.jpg");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.jpg"
+ << "d.jpg");
const KFileItem fileItemC = m_model->fileItem(2);
// Show only the .txt files.
m_model->setNameFilter(".txt");
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt");
// Rename one of the .jpg files.
KFileItem fileItemE = fileItemC;
// Show all files again, and verify that the model has updated the file name.
m_model->setNameFilter(QString());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.jpg" << "e.jpg");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "d.jpg"
+ << "e.jpg");
}
void KFileItemModelTest::testCreateMimeData()
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFile("a/1");
// Expand "a/".
m_model->setExpanded(0, true);
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a" << "1");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a"
+ << "1");
// Verify that creating the MIME data for a child of an expanded folder does
// not cause a crash, see https://bugs.kde.org/show_bug.cgi?id=329119
KItemSet selection;
selection.insert(1);
- QMimeData* mimeData = m_model->createMimeData(selection);
+ QMimeData *mimeData = m_model->createMimeData(selection);
delete mimeData;
}
QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
QSet<QByteArray> modelRoles = m_model->roles();
- modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
m_model->setRoles(modelRoles);
m_testDir->createFile("a2/b/c1.txt");
m_model->setExpanded(0, true);
QVERIFY(m_model->isExpanded(0));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a2" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a2"
+ << "b");
// Expand "a2/b/".
m_model->setExpanded(1, true);
QVERIFY(m_model->isExpanded(1));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a2"
+ << "b"
+ << "c1.txt");
// Simulate that a new item "c2.txt" appears, but that the dir lister's completed()
// signal is not emitted yet.
const QUrl urlB = m_model->fileItem(1).url();
m_model->slotItemsAdded(urlB, KFileItemList() << fileItemC2);
- QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a2"
+ << "b"
+ << "c1.txt");
// Collapse "a2/". This should also remove all its (indirect) children from
// the model and from the model's m_pendingItemsToInsert member.
m_model->setExpanded(0, true);
QVERIFY(m_model->isExpanded(0));
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a2" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a2"
+ << "b");
// Now simulate that a new folder "a1/" is appears, but that the dir lister's
// completed() signal is not emitted yet.
fileItemA1.setUrl(urlA1);
m_model->slotItemsAdded(m_model->directory(), KFileItemList() << fileItemA1);
- QCOMPARE(itemsInModel(), QStringList() << "a2" << "b");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a2"
+ << "b");
// Collapse "a2/". Note that this will cause "a1/" to be added to the model,
// i.e., the index of "a2/" will change from 0 to 1. Check that this does not
// confuse the code which collapses the folder.
m_model->setExpanded(0, false);
- QCOMPARE(itemsInModel(), QStringList() << "a1" << "a2");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a1"
+ << "a2");
QVERIFY(!m_model->isExpanded(0));
QVERIFY(!m_model->isExpanded(1));
}
m_model->loadDirectory(m_testDir->url());
QVERIFY(itemsInsertedSpy.wait());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "d.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "b.txt"
+ << "c.txt"
+ << "d.txt");
const KFileItem fileItemB = m_model->fileItem(1);
m_model->slotItemsDeleted(list);
QVERIFY(m_model->isConsistent());
- QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "c.txt" << "d.txt");
+ QCOMPARE(itemsInModel(),
+ QStringList() << "a.txt"
+ << "c.txt"
+ << "d.txt");
+}
+
+void KFileItemModelTest::testInsertAfterExpand()
+{
+ m_model->m_dirLister->setAutoUpdate(true);
+
+ QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+ QVERIFY(itemsInsertedSpy.isValid());
+ QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
+ QVERIFY(itemsRemovedSpy.isValid());
+ QSignalSpy loadingCompletedSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
+ QVERIFY(loadingCompletedSpy.isValid());
+
+ // Test expanding subfolders in a folder with the items "a/", "a/a/"
+ QSet<QByteArray> originalModelRoles = m_model->roles();
+ QSet<QByteArray> modelRoles = originalModelRoles;
+ modelRoles << "isExpanded"
+ << "isExpandable"
+ << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ m_testDir->createFile("a/b/1");
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(itemsInsertedSpy.wait());
+
+ // So far, the model contains only "a/"
+ QCOMPARE(m_model->count(), 1);
+ QVERIFY(m_model->isExpandable(0));
+ QVERIFY(!m_model->isExpanded(0));
+ QVERIFY(m_model->expandedDirectories().empty());
+
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ {
+ KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 1)); // 1 new item "a/" with index 0
+ QCOMPARE(m_model->expandedParentsCount(0), 0);
+ }
+ itemsInsertedSpy.clear();
+
+ // Expand the folder "a/" -> "a/b" become visible
+ m_model->setExpanded(0, true);
+ QVERIFY(m_model->isExpanded(0));
+ QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(m_model->count(), 2); // 3 items: "a/", "a/a/"
+ QCOMPARE(m_model->expandedDirectories(), QSet<QUrl>({QUrl::fromLocalFile(m_testDir->path() + "/a")}));
+
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ {
+ KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 1)); // 1 new item "a/b" with index 1
+ QCOMPARE(m_model->expandedParentsCount(1), 1);
+ }
+ itemsInsertedSpy.clear();
+
+ // Expand "a/b" -> "a/b/1" becomes visible
+ m_model->setExpanded(1, true);
+ QVERIFY(itemsInsertedSpy.wait());
+ QCOMPARE(m_model->expandedDirectories(), QSet({QUrl::fromLocalFile(m_testDir->path() + "/a"), QUrl::fromLocalFile(m_testDir->path() + "/a/b")}));
+
+ QCOMPARE(itemsInsertedSpy.count(), 1);
+ {
+ KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 1)); // 1 new item "a/b/1" with index 2
+ QCOMPARE(m_model->expandedParentsCount(2), 2);
+ }
+ itemsInsertedSpy.clear();
+
+ // Collapse "a" whilst leaving "b" expanded
+ m_model->setExpanded(0, false);
+
+ // Insert additional files into "a/b/"
+ m_testDir->createFile("a/b/2");
+
+ QVERIFY(!itemsInsertedSpy.wait(5000));
+
+ QCOMPARE(itemsInModel(), {"a"});
+
+ m_model->setExpanded(0, true);
+ ;
+ QTRY_COMPARE(itemsInModel(), QStringList({"a", "b", "1", "2"}));
+
+ QCOMPARE(m_model->expandedParentsCount(0), 0); // a
+ QCOMPARE(m_model->expandedParentsCount(1), 1); // a/b
+ QCOMPARE(m_model->expandedParentsCount(2), 2); // a/b/1
+ QCOMPARE(m_model->expandedParentsCount(3), 2); // a/b/2
+}
+
+void KFileItemModelTest::testCurrentDirRemoved()
+{
+ m_model->m_dirLister->setAutoUpdate(true);
+ QSignalSpy currentDirectoryRemovedSpy(m_model, &KFileItemModel::currentDirectoryRemoved);
+ QVERIFY(currentDirectoryRemovedSpy.isValid());
+ QSignalSpy loadingCompletedSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
+ QVERIFY(loadingCompletedSpy.isValid());
+ QSignalSpy dirListerClearSpy(m_model->m_dirLister, &KCoreDirLister::clear);
+ QVERIFY(dirListerClearSpy.isValid());
+
+ m_testDir->createFiles({"dir/a.txt", "dir/b.txt"});
+ m_model->loadDirectory(QUrl::fromLocalFile(m_testDir->path() + "/dir/"));
+ QVERIFY(loadingCompletedSpy.wait());
+ QCOMPARE(m_model->count(), 2);
+ QVERIFY(m_model->isConsistent());
+
+ m_testDir->removeDir("dir");
+ QVERIFY(currentDirectoryRemovedSpy.wait());
+
+ // dirLister calls clear
+ QCOMPARE(dirListerClearSpy.count(), 2);
+ QVERIFY(m_model->isConsistent());
+ QVERIFY(m_model->m_itemData.isEmpty());
+ QCOMPARE(m_model->count(), 0);
}
QStringList KFileItemModelTest::itemsInModel() const