X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/462982faa361c79daab43ce526ae64646b1ea63c..b4e80645e8e39ef7fcc1545136bad06ab3dd5f3e:/src/tests/kfileitemmodeltest.cpp diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp index 8fd1883ac..34ae2ba87 100644 --- a/src/tests/kfileitemmodeltest.cpp +++ b/src/tests/kfileitemmodeltest.cpp @@ -5,19 +5,20 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include #include -#include #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) +void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(context) @@ -33,7 +34,7 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext& context, const QS fprintf(stderr, "Fatal: %s\n", msg.toLocal8Bit().data()); abort(); default: - break; + break; } } @@ -45,8 +46,9 @@ class KFileItemModelTest : public QObject { Q_OBJECT -private slots: +private Q_SLOTS: void init(); + void initTestCase(); void cleanup(); void testDefaultRoles(); @@ -67,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(); @@ -84,15 +91,23 @@ private slots: void testCollapseFolderWhileLoading(); void testCreateMimeData(); void testDeleteFileMoreThanOnce(); + void testInsertAfterExpand(); + void testCurrentDirRemoved(); + void testSizeSortingAfterRefresh(); private: QStringList itemsInModel() const; private: - KFileItemModel* m_model; - TestDir* m_testDir; + KFileItemModel *m_model; + TestDir *m_testDir; }; +void KFileItemModelTest::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); +} + void KFileItemModelTest::init() { // The item-model tests result in a huge number of debugging @@ -143,14 +158,14 @@ 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() { - QCOMPARE(m_model->groupedSorting(), false); + QCOMPARE(m_model->groupedSorting(), true); } void KFileItemModelTest::testNewItems() @@ -248,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()); } @@ -268,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; } @@ -350,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 @@ -367,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); @@ -392,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. @@ -401,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); @@ -413,7 +441,10 @@ 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() @@ -509,19 +540,20 @@ void KFileItemModelTest::testExpandItems() // 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; - 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 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()); @@ -557,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::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::fromLocalFile(m_testDir->path() + "/a") << QUrl::fromLocalFile(m_testDir->path() + "/a/a")); QCOMPARE(itemsInsertedSpy.count(), 1); itemRangeList = itemsInsertedSpy.takeFirst().at(0).value(); @@ -572,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); @@ -601,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)); @@ -614,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 @@ -649,7 +680,9 @@ void KFileItemModelTest::testExpandParentItems() // a2/b2/c2/ // a2/b2/c2/d2/ QSet 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"}); @@ -721,7 +754,9 @@ void KFileItemModelTest::testMakeExpandedItemHidden() QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved); QSet 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"}); @@ -744,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::SimpleJob* job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo); + KIO::SimpleJob *job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(ok); QVERIFY(itemsRemovedSpy.wait()); @@ -755,7 +790,6 @@ void KFileItemModelTest::testMakeExpandedItemHidden() m_model->setExpanded(0, false); QCOMPARE(m_model->count(), 2); - } void KFileItemModelTest::testRemoveFilteredExpandedItems() @@ -764,7 +798,9 @@ void KFileItemModelTest::testRemoveFilteredExpandedItems() QSet originalModelRoles = m_model->roles(); QSet modelRoles = originalModelRoles; - modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + modelRoles << "isExpanded" + << "isExpandable" + << "expandedParentsCount"; m_model->setRoles(modelRoles); m_testDir->createFiles({"folder/child", "file"}); @@ -778,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"); @@ -800,11 +843,128 @@ 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 roles; + roles.insert("text"); + roles.insert("isExpanded"); + roles.insert("isExpandable"); + roles.insert("expandedParentsCount"); + m_model->setRoles(roles); + + m_testDir->createDir("c/c-2"); + m_testDir->createFile("c/c-2/c-3"); + m_testDir->createFile("c/c-1"); + + m_testDir->createFile("a", "A file", now.addDays(-3)); + m_testDir->createFile("b", "A larger file", now.addDays(0)); + m_testDir->createDir("c", now.addDays(-2)); + m_testDir->createFile("d", "The largest file in this directory", now.addDays(-1)); + m_testDir->createFile("e", "An even larger file", now.addDays(-4)); + + m_model->loadDirectory(m_testDir->url()); + QVERIFY(itemsInsertedSpy.wait()); + + int index = m_model->index(QUrl(m_testDir->url().url() + "/c")); + m_model->setExpanded(index, true); + QVERIFY(itemsInsertedSpy.wait()); + + index = m_model->index(QUrl(m_testDir->url().url() + "/c/c-2")); + m_model->setExpanded(index, true); + QVERIFY(itemsInsertedSpy.wait()); + + // Default: Sort by Name, ascending + QCOMPARE(m_model->sortRole(), QByteArray("text")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QCOMPARE(itemsInModel(), + QStringList() << "c" + << "c-2" + << "c-3" + << "c-1" + << "a" + << "b" + << "d" + << "e"); + + // Sort by Size, ascending, before refresh + m_model->setSortRole("size"); + QCOMPARE(m_model->sortRole(), QByteArray("size")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QCOMPARE(itemsInModel(), + QStringList() << "c" + << "c-2" + << "c-3" + << "c-1" + << "a" + << "b" + << "e" + << "d"); + + // Refresh directory + m_model->refreshDirectory(m_model->directory()); + QVERIFY(itemsInsertedSpy.wait()); + + // Expand folders again + index = m_model->index(QUrl(m_testDir->url().url() + "/c")); + m_model->setExpanded(index, true); + QVERIFY(itemsInsertedSpy.wait()); + + index = m_model->index(QUrl(m_testDir->url().url() + "/c/c-2")); + m_model->setExpanded(index, true); + QVERIFY(itemsInsertedSpy.wait()); + + // Sort by Size, ascending, after refresh + QCOMPARE(m_model->sortRole(), QByteArray("size")); + QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder); + QCOMPARE(itemsInModel(), + QStringList() << "c" + << "c-2" + << "c-3" + << "c-1" + << "a" + << "b" + << "e" + << "d"); } void KFileItemModelTest::testSorting() { + // testDir structure is as follows + // ./ + // ├─ .g/ + // ├─ a + // ├─ b + // ├─ c/ + // │ ├─ c-2/ + // │ │ ├─ c-3 + // │ ├─ c-1 + // ├─ .f + // ├─ d + // ├─ e + QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSignalSpy itemsMovedSpy(m_model, &KFileItemModel::itemsMoved); QVERIFY(itemsMovedSpy.isValid()); @@ -829,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(); + 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")); 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(0, 6)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(0, 6)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 4 << 5 << 0 << 3 << 1 << 2); QCOMPARE(itemsMovedSpy.first().at(0).value(), KItemRange(4, 4)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(4, 4)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 7 << 5 << 4 << 6); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(4, 4)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 7 << 6 << 5 << 4); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(0, 6)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 2 << 4 << 5 << 3 << 0 << 1); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(0, 8)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 7 << 0 << 2 << 3 << 4 << 5 << 6 << 1); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(0, 8)); - QCOMPARE(itemsMovedSpy.takeFirst().at(1).value >(), QList() << 4 << 5 << 0 << 3 << 1 << 2 << 7 << 6); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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 @@ -930,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(4, 4)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 7 << 6 << 5 << 4); + + // '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); + 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::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"}); - // TODO: Sort by other roles; show/hide hidden files + 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(2, 5)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 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(1, 6)); + QCOMPARE(itemsMovedSpy.takeFirst().at(1).value>(), QList() << 4 << 6 << 1 << 2 << 3 << 5); } void KFileItemModelTest::testIndexForKeyboardSearch() { QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); - m_testDir->createFiles({"a", "aa", "Image.jpg", "Image.png", "Text", "Text1", "Text2", "Text11"}); + m_testDir->createFiles({"a", "aa", "Image.jpg", "Image.png", "Text", "Text1", "Text2", "Text11", "U", "Ü", "Üu", "Ž"}); m_model->loadDirectory(m_testDir->url()); QVERIFY(itemsInsertedSpy.wait()); @@ -959,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); @@ -985,6 +1403,12 @@ 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? } @@ -1052,7 +1476,9 @@ void KFileItemModelTest::testRefreshExpandedItem() QVERIFY(itemsChangedSpy.isValid()); QSet 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"}); @@ -1074,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 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 @@ -1092,21 +1753,41 @@ void KFileItemModelTest::testRemoveHiddenItems() 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(); 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(); 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(); @@ -1133,7 +1814,9 @@ void KFileItemModelTest::collapseParentOfHiddenItems() QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved); QSet 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"}); @@ -1157,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); @@ -1169,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"); } /** @@ -1183,7 +1873,9 @@ void KFileItemModelTest::removeParentOfHiddenItems() QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved); QSet 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"}); @@ -1207,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"); } /** @@ -1236,31 +1938,53 @@ void KFileItemModelTest::testGeneralParentChildRelationships() QSignalSpy itemsRemovedSpy(m_model, &KFileItemModel::itemsRemoved); QSet 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(); @@ -1270,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 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); - QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2" << "realChild2"); + QCOMPARE(itemsInModel(), + QStringList() << "parent1" + << "parent2" + << "realChild2"); QCOMPARE(itemsRemovedSpy.count(), 1); arguments = itemsRemovedSpy.takeFirst(); itemRangeList = arguments.at(0).value(); @@ -1328,11 +2094,16 @@ void KFileItemModelTest::testNameRoleGroups() m_testDir->createFiles({"b.txt", "c.txt", "d.txt", "e.txt"}); m_model->setGroupedSorting(true); + m_model->setGroupRole("text"); 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 > expectedGroups; + QList> expectedGroups; expectedGroups << QPair(0, QLatin1String("B")); expectedGroups << QPair(1, QLatin1String("C")); expectedGroups << QPair(2, QLatin1String("D")); @@ -1344,7 +2115,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(0, QLatin1String("A")); @@ -1357,7 +2132,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(0, QLatin1String("A")); @@ -1375,7 +2154,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(0, QLatin1String("A")); @@ -1390,19 +2173,26 @@ void KFileItemModelTest::testNameRoleGroupsWithExpandedItems() QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); - modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + modelRoles << "isExpanded" + << "isExpandable" + << "expandedParentsCount"; m_model->setRoles(modelRoles); m_testDir->createFiles({"a/b.txt", "a/c.txt", "d/e.txt", "d/f.txt"}); m_model->setGroupedSorting(true); + m_model->setGroupRole("text"); + m_model->loadDirectory(m_testDir->url()); QVERIFY(itemsInsertedSpy.wait()); - QCOMPARE(itemsInModel(), QStringList() << "a" << "d"); + QCOMPARE(itemsInModel(), + QStringList() << "a" + << "d"); - QList > expectedGroups; + QList> expectedGroups; expectedGroups << QPair(0, QLatin1String("A")); expectedGroups << QPair(1, QLatin1String("D")); + QCOMPARE(m_model->groups(), expectedGroups); // Verify that expanding "a" and "d" will not change the groups (except for the index of "D"). @@ -1413,13 +2203,23 @@ 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); } @@ -1428,7 +2228,9 @@ void KFileItemModelTest::testInconsistentModel() QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet 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"}); @@ -1441,12 +2243,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 @@ -1464,19 +2272,29 @@ 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() @@ -1491,7 +2309,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". @@ -1510,7 +2331,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. @@ -1525,7 +2348,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". @@ -1551,7 +2377,7 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering() entry[2].fastInsert(KIO::UDSEntry::UDS_USER, "user-a"); for (int i = 0; i < 3; ++i) { - entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms. + entry[i].fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, 0100000); // S_IFREG might not be defined on non-Unix platforms. entry[i].fastInsert(KIO::UDSEntry::UDS_ACCESS, 07777); entry[i].fastInsert(KIO::UDSEntry::UDS_SIZE, 0); entry[i].fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, 0); @@ -1563,7 +2389,10 @@ void KFileItemModelTest::testChangeSortRoleWhileFiltering() 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"); @@ -1574,7 +2403,10 @@ 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() @@ -1585,13 +2417,19 @@ void KFileItemModelTest::testRefreshFilteredItems() m_model->loadDirectory(m_testDir->url()); QVERIFY(itemsInsertedSpy.wait()); - QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.jpg" << "d.jpg"); + QCOMPARE(itemsInModel(), + QStringList() << "a.txt" + << "b.txt" + << "c.jpg" + << "d.jpg"); const KFileItem fileItemC = m_model->fileItem(2); // Show only the .txt files. m_model->setNameFilter(".txt"); - QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt"); + QCOMPARE(itemsInModel(), + QStringList() << "a.txt" + << "b.txt"); // Rename one of the .jpg files. KFileItem fileItemE = fileItemC; @@ -1603,7 +2441,11 @@ 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() @@ -1611,7 +2453,9 @@ void KFileItemModelTest::testCreateMimeData() QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); - modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + modelRoles << "isExpanded" + << "isExpandable" + << "expandedParentsCount"; m_model->setRoles(modelRoles); m_testDir->createFile("a/1"); @@ -1623,13 +2467,15 @@ 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); - QMimeData* mimeData = m_model->createMimeData(selection); + QMimeData *mimeData = m_model->createMimeData(selection); delete mimeData; } @@ -1638,7 +2484,9 @@ void KFileItemModelTest::testCollapseFolderWhileLoading() QSignalSpy itemsInsertedSpy(m_model, &KFileItemModel::itemsInserted); QSet modelRoles = m_model->roles(); - modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount"; + modelRoles << "isExpanded" + << "isExpandable" + << "expandedParentsCount"; m_model->setRoles(modelRoles); m_testDir->createFile("a2/b/c1.txt"); @@ -1651,13 +2499,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. @@ -1670,7 +2523,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. @@ -1689,7 +2545,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. @@ -1700,13 +2558,17 @@ 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)); } @@ -1719,7 +2581,11 @@ void KFileItemModelTest::testDeleteFileMoreThanOnce() 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); @@ -1729,7 +2595,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 originalModelRoles = m_model->roles(); + QSet 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(); + 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::fromLocalFile(m_testDir->path() + "/a")})); + + QCOMPARE(itemsInsertedSpy.count(), 1); + { + KItemRangeList itemRangeList = itemsInsertedSpy.takeFirst().at(0).value(); + 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(); + 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