void myMessageOutput(QtMsgType type, const char* msg)
{
switch (type) {
- case QtDebugMsg:
- break;
- case QtWarningMsg:
- break;
- case QtCriticalMsg:
- fprintf(stderr, "Critical: %s\n", msg);
- break;
- case QtFatalMsg:
- fprintf(stderr, "Fatal: %s\n", msg);
- abort();
- default:
+ case QtDebugMsg:
break;
+ case QtWarningMsg:
+ break;
+ case QtCriticalMsg:
+ fprintf(stderr, "Critical: %s\n", msg);
+ break;
+ case QtFatalMsg:
+ fprintf(stderr, "Fatal: %s\n", msg);
+ abort();
+ default:
+ break;
}
- }
+}
namespace {
const int DefaultTimeout = 5000;
void testExpandItems();
void testExpandParentItems();
void testSorting();
-
void testIndexForKeyboardSearch();
-
void testNameFilter();
+ void testEmptyPath();
+ void testRemoveHiddenItems();
+ void collapseParentOfHiddenItems();
+ void removeParentOfHiddenItems();
private:
bool isModelConsistent() const;
void KFileItemModelTest::testDefaultRoles()
{
const QSet<QByteArray> roles = m_model->roles();
- QCOMPARE(roles.count(), 2);
- QVERIFY(roles.contains("name"));
+ QCOMPARE(roles.count(), 3);
+ QVERIFY(roles.contains("text"));
QVERIFY(roles.contains("isDir"));
+ QVERIFY(roles.contains("isLink"));
}
void KFileItemModelTest::testDefaultSortRole()
{
- QCOMPARE(m_model->sortRole(), QByteArray("name"));
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
QStringList files;
files << "c.txt" << "a.txt" << "b.txt";
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3);
- QCOMPARE(m_model->data(0)["name"].toString(), QString("a.txt"));
- QCOMPARE(m_model->data(1)["name"].toString(), QString("b.txt"));
- QCOMPARE(m_model->data(2)["name"].toString(), QString("c.txt"));
+ QCOMPARE(m_model->data(0)["text"].toString(), QString("a.txt"));
+ QCOMPARE(m_model->data(1)["text"].toString(), QString("b.txt"));
+ QCOMPARE(m_model->data(2)["text"].toString(), QString("c.txt"));
}
void KFileItemModelTest::testDefaultGroupedSorting()
// Changing the value of a sort-role must result in
// a reordering of the items.
- QCOMPARE(m_model->sortRole(), QByteArray("name"));
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
QStringList files;
files << "a.txt" << "b.txt" << "c.txt";
// Store the URLs of all folders in a set.
QSet<KUrl> allFolders;
- allFolders << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a") << KUrl(m_testDir->name() + "a/a-1");
+ allFolders << KUrl(m_testDir->name() + 'a') << KUrl(m_testDir->name() + "a/a") << KUrl(m_testDir->name() + "a/a-1");
m_model->loadDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QVERIFY(m_model->isExpanded(0));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/a/", "a/a-1/"
- QCOMPARE(m_model->expandedDirectories(), QSet<KUrl>() << KUrl(m_testDir->name() + "a"));
+ QCOMPARE(m_model->expandedDirectories(), QSet<KUrl>() << KUrl(m_testDir->name() + 'a'));
QCOMPARE(spyInserted.count(), 1);
KItemRangeList itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
QVERIFY(m_model->isExpanded(1));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 4); // 4 items: "a/", "a/a/", "a/a/1", "a/a-1/"
- QCOMPARE(m_model->expandedDirectories(), QSet<KUrl>() << KUrl(m_testDir->name() + "a") << KUrl(m_testDir->name() + "a/a"));
+ QCOMPARE(m_model->expandedDirectories(), QSet<KUrl>() << KUrl(m_testDir->name() + 'a') << KUrl(m_testDir->name() + "a/a"));
QCOMPARE(spyInserted.count(), 1);
itemRangeList = spyInserted.takeFirst().at(0).value<KItemRangeList>();
m_model->setExpanded(0, false);
QVERIFY(!m_model->isExpanded(0));
QCOMPARE(m_model->count(), 1);
- QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + "a"))); // TODO: Make sure that child URLs are also removed
+ QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a'))); // TODO: Make sure that child URLs are also removed
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
QDateTime now = QDateTime::currentDateTime();
QSet<QByteArray> roles;
- roles.insert("name");
+ roles.insert("text");
roles.insert("isExpanded");
roles.insert("isExpandable");
roles.insert("expandedParentsCount");
m_model->loadDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
- int index = m_model->index(KUrl(m_testDir->url().url() + "c"));
+ int index = m_model->index(KUrl(m_testDir->url().url() + 'c'));
m_model->setExpanded(index, true);
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
// Default: Sort by Name, ascending
- QCOMPARE(m_model->sortRole(), QByteArray("name"));
+ QCOMPARE(m_model->sortRole(), QByteArray("text"));
QCOMPARE(m_model->sortOrder(), Qt::AscendingOrder);
QVERIFY(m_model->sortDirectoriesFirst());
QVERIFY(!m_model->showHiddenFiles());
// Sort by Name, ascending, 'Sort Folders First' disabled
m_model->setSortDirectoriesFirst(false);
- QCOMPARE(m_model->sortRole(), QByteArray("name"));
+ 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(spyItemsMoved.count(), 1);
// Sort by Name, descending
m_model->setSortDirectoriesFirst(true);
m_model->setSortOrder(Qt::DescendingOrder);
- QCOMPARE(m_model->sortRole(), QByteArray("name"));
+ 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(spyItemsMoved.count(), 2);
QCOMPARE(spyItemsMoved.takeFirst().at(1).value<QList<int> >(), QList<int>() << 2 << 4 << 5 << 3 << 0 << 1 << 6 << 7);
// Sort by Name, ascending, 'Sort Folders First' disabled
- m_model->setSortRole("name");
+ 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(m_model->count(), 5);
}
+/**
+ * Verifies that we do not crash when adding a KFileItem with an empty path.
+ * Before this issue was fixed, KFileItemModel::expandedParentsCountCompare()
+ * tried to always read the first character of the path, even if the path is empty.
+ */
+void KFileItemModelTest::testEmptyPath()
+{
+ QSet<QByteArray> roles;
+ roles.insert("text");
+ roles.insert("isExpanded");
+ roles.insert("isExpandable");
+ roles.insert("expandedParentsCount");
+ m_model->setRoles(roles);
+
+ const KUrl emptyUrl;
+ QVERIFY(emptyUrl.path().isEmpty());
+
+ const KUrl url("file:///test/");
+
+ KFileItemList items;
+ items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown);
+ m_model->slotNewItems(items);
+ m_model->slotCompleted();
+}
+
+/**
+ * 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
+ */
+void KFileItemModelTest::testRemoveHiddenItems()
+{
+ m_testDir->createDir(".a");
+ m_testDir->createDir(".b");
+ m_testDir->createDir("c");
+ m_testDir->createDir("d");
+ m_testDir->createFiles(QStringList() << ".f" << ".g" << "h" << "i");
+
+ QSignalSpy spyItemsInserted(m_model, SIGNAL(itemsInserted(KItemRangeList)));
+ QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+
+ m_model->setShowHiddenFiles(true);
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i");
+ QCOMPARE(spyItemsInserted.count(), 1);
+ QCOMPARE(spyItemsRemoved.count(), 0);
+ KItemRangeList itemRangeList = spyItemsInserted.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 8));
+
+ m_model->setShowHiddenFiles(false);
+ QCOMPARE(itemsInModel(), QStringList() << "c" << "d" << "h" << "i");
+ QCOMPARE(spyItemsInserted.count(), 0);
+ QCOMPARE(spyItemsRemoved.count(), 1);
+ itemRangeList = spyItemsRemoved.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 2) << KItemRange(4, 2));
+
+ m_model->setShowHiddenFiles(true);
+ QCOMPARE(itemsInModel(), QStringList() << ".a" << ".b" << "c" << "d" <<".f" << ".g" << "h" << "i");
+ QCOMPARE(spyItemsInserted.count(), 1);
+ QCOMPARE(spyItemsRemoved.count(), 0);
+ itemRangeList = spyItemsInserted.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 2) << KItemRange(2, 2));
+
+ m_model->clear();
+ QCOMPARE(itemsInModel(), QStringList());
+ QCOMPARE(spyItemsInserted.count(), 0);
+ QCOMPARE(spyItemsRemoved.count(), 1);
+ itemRangeList = spyItemsRemoved.takeFirst().at(0).value<KItemRangeList>();
+ QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(0, 8));
+
+ // Hiding hidden files makes the dir lister emit its itemsDeleted signal.
+ // Verify that this does not make the model crash.
+ m_model->setShowHiddenFiles(false);
+}
+
+/**
+ * Verify that filtered items are removed when their parent is collapsed.
+ */
+void KFileItemModelTest::collapseParentOfHiddenItems()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ files << "a/1" << "a/b/1" << "a/b/c/1" << "a/b/c/d/1";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 1); // Only "a/"
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/1"
+
+ // Expand "a/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/b/", "a/b/c", "a/b/1", "a/1"
+
+ // Expand "a/b/c/".
+ m_model->setExpanded(2, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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.
+ m_model->setNameFilter("xyz");
+ QCOMPARE(m_model->count(), 3);
+ QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c");
+
+ // Collapse the folder "a/".
+ QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+ m_model->setExpanded(0, false);
+ QCOMPARE(spyItemsRemoved.count(), 1);
+ QCOMPARE(m_model->count(), 1);
+ QCOMPARE(itemsInModel(), QStringList() << "a");
+
+ // Remove the filter -> no files should appear (and we should not get a crash).
+ m_model->setNameFilter(QString());
+ QCOMPARE(m_model->count(), 1);
+}
+
+/**
+ * Verify that filtered items are removed when their parent is deleted.
+ */
+void KFileItemModelTest::removeParentOfHiddenItems()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ files << "a/1" << "a/b/1" << "a/b/c/1" << "a/b/c/d/1";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 1); // Only "a/"
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 3); // 3 items: "a/", "a/b/", "a/1"
+
+ // Expand "a/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(m_model->count(), 5); // 5 items: "a/", "a/b/", "a/b/c", "a/b/1", "a/1"
+
+ // Expand "a/b/c/".
+ m_model->setExpanded(2, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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.
+ m_model->setNameFilter("xyz");
+ QCOMPARE(m_model->count(), 3);
+ QCOMPARE(itemsInModel(), QStringList() << "a" << "b" << "c");
+
+ // Simulate the deletion of the directory "a/b/".
+ QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+ m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1));
+ QCOMPARE(spyItemsRemoved.count(), 1);
+ QCOMPARE(m_model->count(), 1);
+ QCOMPARE(itemsInModel(), QStringList() << "a");
+
+ // Remove the filter -> only the file "a/1" should appear.
+ m_model->setNameFilter(QString());
+ QCOMPARE(m_model->count(), 2);
+ QCOMPARE(itemsInModel(), QStringList() << "a" << "1");
+}
+
bool KFileItemModelTest::isModelConsistent() const
{
if (m_model->m_items.count() != m_model->m_itemData.count()) {
{
QStringList items;
for (int i = 0; i < m_model->count(); i++) {
- items << m_model->data(i).value("name").toString();
+ items << m_model->data(i).value("text").toString();
}
return items;
}