]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/tests/kfileitemmodeltest.cpp
GIT_SILENT Update Appstream for new release
[dolphin.git] / src / tests / kfileitemmodeltest.cpp
index 59ff63069b47eed1869b9ffbb77458e6ab4cd401..4c6db590d8c78bfb40b1d42a6f8d7b9a1a24823c 100644 (file)
@@ -1,37 +1,26 @@
-/***************************************************************************
- *   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:
@@ -45,7 +34,7 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QS
         fprintf(stderr, "Fatal: %s\n", msg.toLocal8Bit().data());
         abort();
     default:
-       break;
+        break;
     }
 }
 
@@ -57,8 +46,9 @@ class KFileItemModelTest : public QObject
 {
     Q_OBJECT
 
-private slots:
+private Q_SLOTS:
     void init();
+    void initTestCase();
     void cleanup();
 
     void testDefaultRoles();
@@ -79,10 +69,15 @@ private slots:
     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();
@@ -96,15 +91,23 @@ private slots:
     void testCollapseFolderWhileLoading();
     void testCreateMimeData();
     void testDeleteFileMoreThanOnce();
+    void testInsertAfterExpand();
+    void testCurrentDirRemoved();
+    void testSizeSortingAfterRefresh();
 
 private:
     QStringList itemsInModel() const;
 
 private:
-    KFileItemModelm_model;
-    TestDirm_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
@@ -126,24 +129,25 @@ void KFileItemModelTest::init()
 void KFileItemModelTest::cleanup()
 {
     delete m_model;
-    m_model = 0;
+    m_model = nullptr;
 
     delete m_testDir;
-    m_testDir = 0;
+    m_testDir = nullptr;
 }
 
 void KFileItemModelTest::testDefaultRoles()
 {
     const QSet<QByteArray> roles = m_model->roles();
-    QCOMPARE(roles.count(), 3);
+    QCOMPARE(roles.count(), 4);
     QVERIFY(roles.contains("text"));
     QVERIFY(roles.contains("isDir"));
     QVERIFY(roles.contains("isLink"));
+    QVERIFY(roles.contains("isHidden"));
 }
 
 void KFileItemModelTest::testDefaultSortRole()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
     QVERIFY(itemsInsertedSpy.isValid());
 
     QCOMPARE(m_model->sortRole(), QByteArray("text"));
@@ -154,9 +158,9 @@ void KFileItemModelTest::testDefaultSortRole()
     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()
@@ -166,7 +170,7 @@ void KFileItemModelTest::testDefaultGroupedSorting()
 
 void KFileItemModelTest::testNewItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     m_testDir->createFiles({"a.txt", "b.txt", "c.txt"});
 
@@ -180,8 +184,8 @@ void KFileItemModelTest::testNewItems()
 
 void KFileItemModelTest::testRemoveItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
 
     m_testDir->createFiles({"a.txt", "b.txt"});
     m_model->loadDirectory(m_testDir->url());
@@ -198,9 +202,9 @@ void KFileItemModelTest::testRemoveItems()
 
 void KFileItemModelTest::testDirLoadingCompleted()
 {
-    QSignalSpy loadingCompletedSpy(m_model, SIGNAL(directoryLoadingCompleted()));
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy loadingCompletedSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
 
     m_testDir->createFiles({"a.txt", "b.txt", "c.txt"});
 
@@ -241,9 +245,9 @@ void KFileItemModelTest::testDirLoadingCompleted()
 
 void KFileItemModelTest::testSetData()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
     QVERIFY(itemsInsertedSpy.isValid());
-    QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList, QSet<QByteArray>)));
+    QSignalSpy itemsChangedSpy(m_model, &KFileItemModel::itemsChanged);
     QVERIFY(itemsChangedSpy.isValid());
 
     m_testDir->createFile("a.txt");
@@ -259,8 +263,8 @@ void KFileItemModelTest::testSetData()
     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());
 }
 
@@ -279,11 +283,11 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole_data()
     // 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;
 }
 
@@ -296,9 +300,9 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
     QFETCH(int, ratingIndex1);
     QFETCH(int, ratingIndex2);
 
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
     QVERIFY(itemsInsertedSpy.isValid());
-    QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
     QVERIFY(itemsMovedSpy.isValid());
 
     // Changing the value of a sort-role must result in
@@ -351,8 +355,8 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
 
 void KFileItemModelTest::testChangeSortRole()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
     QVERIFY(itemsMovedSpy.isValid());
 
     QCOMPARE(m_model->sortRole(), QByteArray("text"));
@@ -361,7 +365,10 @@ void KFileItemModelTest::testChangeSortRole()
 
     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
@@ -378,10 +385,14 @@ void KFileItemModelTest::testChangeSortRole()
     // 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);
@@ -391,8 +402,8 @@ void KFileItemModelTest::testChangeSortRole()
 
 void KFileItemModelTest::testResortAfterChangingName()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
     QVERIFY(itemsMovedSpy.isValid());
 
     // We sort by size in a directory where all files have the same size.
@@ -403,7 +414,10 @@ void KFileItemModelTest::testResortAfterChangingName()
 
     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.
@@ -412,7 +426,10 @@ void KFileItemModelTest::testResortAfterChangingName()
     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);
@@ -424,12 +441,15 @@ void KFileItemModelTest::testResortAfterChangingName()
     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()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     // KFileItemModel prevents that inserting a punch of items sequentially
     // results in an itemsInserted()-signal for each item. Instead internally
@@ -449,9 +469,9 @@ 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);
 
@@ -459,7 +479,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
         }
 
         m_model->m_dirLister->updateDirectory(m_testDir->url());
-        if (itemsInsertedSpy.count() == 0) {
+        if (itemsInsertedSpy.isEmpty()) {
             QVERIFY(itemsInsertedSpy.wait());
         }
 
@@ -471,7 +491,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
 
 void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     m_testDir->createFiles({"B", "E", "G"});
 
@@ -510,29 +530,30 @@ void KFileItemModelTest::testItemRangeConsistencyWhenInsertingItems()
 
 void KFileItemModelTest::testExpandItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
     QVERIFY(itemsInsertedSpy.isValid());
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
     QVERIFY(itemsRemovedSpy.isValid());
-    QSignalSpy loadingCompletedSpy(m_model, SIGNAL(directoryLoadingCompleted()));
+    QSignalSpy loadingCompletedSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
     QVERIFY(loadingCompletedSpy.isValid());
 
     // 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());
@@ -568,9 +589,8 @@ void KFileItemModelTest::testExpandItems()
     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>();
@@ -583,7 +603,7 @@ void KFileItemModelTest::testExpandItems()
     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);
@@ -612,7 +632,7 @@ void KFileItemModelTest::testExpandItems()
     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));
@@ -625,11 +645,11 @@ void KFileItemModelTest::testExpandItems()
     // 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
@@ -647,8 +667,8 @@ void KFileItemModelTest::testExpandItems()
 
 void KFileItemModelTest::testExpandParentItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy loadingCompletedSpy(m_model, SIGNAL(directoryLoadingCompleted()));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy loadingCompletedSpy(m_model, &KFileItemModel::directoryLoadingCompleted);
     QVERIFY(loadingCompletedSpy.isValid());
 
     // Create a tree structure of folders:
@@ -660,7 +680,9 @@ void KFileItemModelTest::testExpandParentItems()
     // 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"});
@@ -728,11 +750,13 @@ void KFileItemModelTest::testExpandParentItems()
  */
 void KFileItemModelTest::testMakeExpandedItemHidden()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    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"});
@@ -755,7 +779,7 @@ void KFileItemModelTest::testMakeExpandedItemHidden()
     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::SimpleJobjob = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
+    KIO::SimpleJob *job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
     bool ok = job->exec();
     QVERIFY(ok);
     QVERIFY(itemsRemovedSpy.wait());
@@ -766,16 +790,17 @@ void KFileItemModelTest::testMakeExpandedItemHidden()
 
     m_model->setExpanded(0, false);
     QCOMPARE(m_model->count(), 2);
-
 }
 
 void KFileItemModelTest::testRemoveFilteredExpandedItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     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"});
@@ -789,17 +814,24 @@ void KFileItemModelTest::testRemoveFilteredExpandedItems()
     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");
@@ -811,13 +843,130 @@ void KFileItemModelTest::testRemoveFilteredExpandedItems()
 
     // 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()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    // 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());
 
     // Create some files with different sizes and modification times to check the different sorting options
@@ -840,100 +989,182 @@ void KFileItemModelTest::testSorting()
     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
@@ -941,19 +1172,192 @@ void KFileItemModelTest::testSorting()
     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, SIGNAL(itemsInserted(KItemRangeList)));
+    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());
@@ -970,6 +1374,9 @@ void KFileItemModelTest::testIndexForKeyboardSearch()
     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);
@@ -996,12 +1403,18 @@ void KFileItemModelTest::testIndexForKeyboardSearch()
     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?
 }
 
 void KFileItemModelTest::testNameFilter()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     m_testDir->createFiles({"A1", "A2", "Abc", "Bcd", "Cde"});
 
@@ -1058,12 +1471,14 @@ void KFileItemModelTest::testEmptyPath()
  */
 void KFileItemModelTest::testRefreshExpandedItem()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList, QSet<QByteArray>)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsChangedSpy(m_model, &KFileItemModel::itemsChanged);
     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"});
@@ -1085,6 +1500,241 @@ void KFileItemModelTest::testRefreshExpandedItem()
     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
@@ -1097,27 +1747,47 @@ void KFileItemModelTest::testRemoveHiddenItems()
     m_testDir->createDir("d");
     m_testDir->createFiles({".f", ".g", "h", "i"});
 
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved);
 
     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>();
@@ -1140,11 +1810,13 @@ void KFileItemModelTest::testRemoveHiddenItems()
  */
 void KFileItemModelTest::collapseParentOfHiddenItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    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"});
@@ -1168,11 +1840,16 @@ void KFileItemModelTest::collapseParentOfHiddenItems()
     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);
@@ -1180,9 +1857,11 @@ void KFileItemModelTest::collapseParentOfHiddenItems()
     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");
 }
 
 /**
@@ -1190,11 +1869,13 @@ void KFileItemModelTest::collapseParentOfHiddenItems()
  */
 void KFileItemModelTest::removeParentOfHiddenItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    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"});
@@ -1218,22 +1899,32 @@ void KFileItemModelTest::removeParentOfHiddenItems()
     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");
 }
 
 /**
@@ -1243,35 +1934,57 @@ void KFileItemModelTest::removeParentOfHiddenItems()
  */
 void KFileItemModelTest::testGeneralParentChildRelationships()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    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();
@@ -1281,35 +1994,77 @@ void KFileItemModelTest::testGeneralParentChildRelationships()
 
     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");
+    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");
-
-    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>();
@@ -1330,10 +2085,10 @@ void KFileItemModelTest::testGeneralParentChildRelationships()
 
 void KFileItemModelTest::testNameRoleGroups()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
-    QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
+    QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved);
     QVERIFY(itemsMovedSpy.isValid());
-    QSignalSpy groupsChangedSpy(m_model, SIGNAL(groupsChanged()));
+    QSignalSpy groupsChangedSpy(m_model, &KFileItemModel::groupsChanged);
     QVERIFY(groupsChangedSpy.isValid());
 
     m_testDir->createFiles({"b.txt", "c.txt", "d.txt", "e.txt"});
@@ -1341,9 +2096,13 @@ void KFileItemModelTest::testNameRoleGroups()
     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"));
@@ -1355,7 +2114,11 @@ void KFileItemModelTest::testNameRoleGroups()
     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"));
@@ -1368,7 +2131,11 @@ void KFileItemModelTest::testNameRoleGroups()
     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"));
@@ -1386,7 +2153,11 @@ void KFileItemModelTest::testNameRoleGroups()
 
     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"));
@@ -1398,10 +2169,12 @@ void KFileItemModelTest::testNameRoleGroups()
 
 void KFileItemModelTest::testNameRoleGroupsWithExpandedItems()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    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"});
@@ -1409,9 +2182,11 @@ void KFileItemModelTest::testNameRoleGroupsWithExpandedItems()
     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);
@@ -1424,22 +2199,34 @@ void KFileItemModelTest::testNameRoleGroupsWithExpandedItems()
     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);
 }
 
 void KFileItemModelTest::testInconsistentModel()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    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"});
@@ -1452,12 +2239,18 @@ void KFileItemModelTest::testInconsistentModel()
     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
@@ -1475,24 +2268,34 @@ void KFileItemModelTest::testInconsistentModel()
     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()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     QSet<QByteArray> modelRoles = m_model->roles();
     modelRoles << "owner";
@@ -1502,7 +2305,10 @@ 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".
@@ -1521,7 +2327,9 @@ void KFileItemModelTest::testChangeRolesForFilteredItems()
 
     // 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.
@@ -1536,7 +2344,10 @@ void KFileItemModelTest::testChangeRolesForFilteredItems()
 
     // 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".
@@ -1550,30 +2361,34 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering()
 {
     KFileItemList items;
 
-    KIO::UDSEntry entry;
-    entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000);    // S_IFREG might not be defined on non-Unix platforms.
-    entry.insert(KIO::UDSEntry::UDS_ACCESS, 07777);
-    entry.insert(KIO::UDSEntry::UDS_SIZE, 0);
-    entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0);
-    entry.insert(KIO::UDSEntry::UDS_GROUP, "group");
-    entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, 0);
+    KIO::UDSEntry entry[3];
 
-    entry.insert(KIO::UDSEntry::UDS_NAME, "a.txt");
-    entry.insert(KIO::UDSEntry::UDS_USER, "user-b");
-    items.append(KFileItem(entry, m_testDir->url(), false, true));
+    entry[0].fastInsert(KIO::UDSEntry::UDS_NAME, "a.txt");
+    entry[0].fastInsert(KIO::UDSEntry::UDS_USER, "user-b");
 
-    entry.insert(KIO::UDSEntry::UDS_NAME, "b.txt");
-    entry.insert(KIO::UDSEntry::UDS_USER, "user-c");
-    items.append(KFileItem(entry, m_testDir->url(), false, true));
+    entry[1].fastInsert(KIO::UDSEntry::UDS_NAME, "b.txt");
+    entry[1].fastInsert(KIO::UDSEntry::UDS_USER, "user-c");
 
-    entry.insert(KIO::UDSEntry::UDS_NAME, "c.txt");
-    entry.insert(KIO::UDSEntry::UDS_USER, "user-a");
-    items.append(KFileItem(entry, m_testDir->url(), false, true));
+    entry[2].fastInsert(KIO::UDSEntry::UDS_NAME, "c.txt");
+    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_ACCESS, 07777);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_SIZE, 0);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0);
+        entry[i].fastInsert(KIO::UDSEntry::UDS_GROUP, "group");
+        entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, 0);
+        items.append(KFileItem(entry[i], m_testDir->url(), false, true));
+    }
 
     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");
@@ -1584,24 +2399,33 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering()
 
     // 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()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     m_testDir->createFiles({"a.txt", "b.txt", "c.jpg", "d.jpg"});
 
     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;
@@ -1613,15 +2437,21 @@ void KFileItemModelTest::testRefreshFilteredItems()
 
     // 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, SIGNAL(itemsInserted(KItemRangeList)));
+    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");
@@ -1633,22 +2463,26 @@ void KFileItemModelTest::testCreateMimeData()
     // 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);
-    QMimeDatamimeData = m_model->createMimeData(selection);
+    QMimeData *mimeData = m_model->createMimeData(selection);
     delete mimeData;
 }
 
 void KFileItemModelTest::testCollapseFolderWhileLoading()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    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");
@@ -1661,13 +2495,18 @@ void KFileItemModelTest::testCollapseFolderWhileLoading()
     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.
@@ -1680,7 +2519,10 @@ void KFileItemModelTest::testCollapseFolderWhileLoading()
 
     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.
@@ -1699,7 +2541,9 @@ void KFileItemModelTest::testCollapseFolderWhileLoading()
     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.
@@ -1710,26 +2554,34 @@ void KFileItemModelTest::testCollapseFolderWhileLoading()
     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));
 }
 
 void KFileItemModelTest::testDeleteFileMoreThanOnce()
 {
-    QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+    QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted);
 
     m_testDir->createFiles({"a.txt", "b.txt", "c.txt", "d.txt"});
 
     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);
 
@@ -1739,7 +2591,122 @@ void KFileItemModelTest::testDeleteFileMoreThanOnce()
     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