X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/0dd0b65bf02f52da0d2e9d270160e69b81a357ca..ce7852fb2352155e4373f9a41f0a01549985bd45:/src/tests/kfileitemmodeltest.cpp diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 471818d98..679b8ab3a 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -1,37 +1,26 @@ -/*************************************************************************** - * Copyright (C) 2011 by Peter Penz * - * Copyright (C) 2011 by Frank Reininghaus * - * * - * 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 + * SPDX-FileCopyrightText: 2011 Frank Reininghaus + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include #include #include +#include #include #include +#include #include #include "kitemviews/kfileitemmodel.h" -#include "kitemviews/private/kfileitemmodeldirlister.h" #include "testdir.h" void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) { - Q_UNUSED(context); + Q_UNUSED(context) switch (type) { case QtDebugMsg: @@ -57,8 +46,9 @@ class KFileItemModelTest : public QObject { Q_OBJECT -private slots: +private Q_SLOTS: void init(); + void initTestCase(); void cleanup(); void testDefaultRoles(); @@ -83,6 +73,10 @@ private slots: void testNameFilter(); void testEmptyPath(); void testRefreshExpandedItem(); + void testAddItemToFilteredExpandedFolder(); + void testDeleteItemsWithExpandedFolderWithFilter(); + void testRefreshItemsWithFilter(); + void testRefreshExpandedFolderWithFilter(); void testRemoveHiddenItems(); void collapseParentOfHiddenItems(); void removeParentOfHiddenItems(); @@ -105,6 +99,11 @@ private: TestDir* m_testDir; }; +void KFileItemModelTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); +} + void KFileItemModelTest::init() { // The item-model tests result in a huge number of debugging @@ -144,7 +143,7 @@ void KFileItemModelTest::testDefaultRoles() 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")); @@ -167,7 +166,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"}); @@ -181,8 +180,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()); @@ -199,9 +198,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"}); @@ -242,9 +241,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))); + QSignalSpy itemsChangedSpy(m_model, &KFileItemModel::itemsChanged); QVERIFY(itemsChangedSpy.isValid()); m_testDir->createFile("a.txt"); @@ -297,9 +296,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))); + QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved); QVERIFY(itemsMovedSpy.isValid()); // Changing the value of a sort-role must result in @@ -352,8 +351,8 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole() void KFileItemModelTest::testChangeSortRole() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); - QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); + QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved); QVERIFY(itemsMovedSpy.isValid()); QCOMPARE(m_model->sortRole(), QByteArray("text")); @@ -392,8 +391,8 @@ void KFileItemModelTest::testChangeSortRole() void KFileItemModelTest::testResortAfterChangingName() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); - QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + 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. @@ -430,7 +429,7 @@ void KFileItemModelTest::testResortAfterChangingName() 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 @@ -450,9 +449,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); @@ -460,7 +459,7 @@ void KFileItemModelTest::testModelConsistencyWhenInsertingItems() } m_model->m_dirLister->updateDirectory(m_testDir->url()); - if (itemsInsertedSpy.count() == 0) { + if (itemsInsertedSpy.isEmpty()) { QVERIFY(itemsInsertedSpy.wait()); } @@ -472,7 +471,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"}); @@ -511,17 +510,17 @@ 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 originalModelRoles = m_model->roles(); QSet modelRoles = originalModelRoles; @@ -648,8 +647,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: @@ -729,8 +728,8 @@ 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 modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -772,7 +771,7 @@ void KFileItemModelTest::testMakeExpandedItemHidden() void KFileItemModelTest::testRemoveFilteredExpandedItems() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet originalModelRoles = m_model->roles(); QSet modelRoles = originalModelRoles; @@ -817,8 +816,21 @@ void KFileItemModelTest::testRemoveFilteredExpandedItems() void KFileItemModelTest::testSorting() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); - QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + // 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 @@ -841,17 +853,27 @@ 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(); + 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(); + 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(); + QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 1)); // Default: Sort by Name, ascending QCOMPARE(m_model->sortRole(), QByteArray("text")); @@ -947,12 +969,51 @@ void KFileItemModelTest::testSorting() QCOMPARE(itemsMovedSpy.first().at(0).value(), KItemRange(4, 4)); QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 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() << 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(4, 5)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 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(4, 2)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 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(4, 4)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 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(0, 10)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7 << 9 << 8); + } 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"}); @@ -1002,7 +1063,7 @@ void KFileItemModelTest::testIndexForKeyboardSearch() void KFileItemModelTest::testNameFilter() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); m_testDir->createFiles({"A1", "A2", "Abc", "Bcd", "Cde"}); @@ -1059,8 +1120,8 @@ void KFileItemModelTest::testEmptyPath() */ void KFileItemModelTest::testRefreshExpandedItem() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); - QSignalSpy itemsChangedSpy(m_model, SIGNAL(itemsChanged(KItemRangeList, QSet))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); + QSignalSpy itemsChangedSpy(m_model, &KFileItemModel::itemsChanged); QVERIFY(itemsChangedSpy.isValid()); QSet modelRoles = m_model->roles(); @@ -1086,6 +1147,219 @@ 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 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 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 arguments = itemsChangedSpy.takeLast(); + KItemRangeList itemRangeList = arguments.at(0).value(); + + // 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 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 @@ -1098,8 +1372,8 @@ 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()); @@ -1141,8 +1415,8 @@ 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 modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1169,11 +1443,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); @@ -1181,9 +1460,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"); } /** @@ -1191,8 +1472,8 @@ 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 modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1219,17 +1500,22 @@ 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"); // 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()); @@ -1244,8 +1530,8 @@ 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 modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1292,21 +1578,22 @@ void KFileItemModelTest::testGeneralParentChildRelationships() 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"); - 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. + // 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 arguments = itemsRemovedSpy.takeFirst(); KItemRangeList itemRangeList = arguments.at(0).value(); - 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); @@ -1331,10 +1618,10 @@ void KFileItemModelTest::testGeneralParentChildRelationships() void KFileItemModelTest::testNameRoleGroups() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); - QSignalSpy itemsMovedSpy(m_model, SIGNAL(itemsMoved(KItemRange,QList))); + 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"}); @@ -1399,7 +1686,7 @@ void KFileItemModelTest::testNameRoleGroups() void KFileItemModelTest::testNameRoleGroupsWithExpandedItems() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1437,7 +1724,7 @@ void KFileItemModelTest::testNameRoleGroupsWithExpandedItems() void KFileItemModelTest::testInconsistentModel() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1493,7 +1780,7 @@ void KFileItemModelTest::testInconsistentModel() void KFileItemModelTest::testChangeRolesForFilteredItems() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); modelRoles << "owner"; @@ -1551,25 +1838,26 @@ 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(); @@ -1590,7 +1878,7 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering() 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"}); @@ -1619,7 +1907,7 @@ void KFileItemModelTest::testRefreshFilteredItems() void KFileItemModelTest::testCreateMimeData() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1646,7 +1934,7 @@ void KFileItemModelTest::testCreateMimeData() void KFileItemModelTest::testCollapseFolderWhileLoading() { - QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList))); + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; @@ -1724,7 +2012,7 @@ void KFileItemModelTest::testCollapseFolderWhileLoading() 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"});