#include <qtest_kde.h>
-#include <KDirLister>
#include <kio/job.h>
#include "kitemviews/kfileitemmodel.h"
void testNameRoleGroups();
void testNameRoleGroupsWithExpandedItems();
void testInconsistentModel();
+ void testChangeRolesForFilteredItems();
+ void testChangeSortRoleWhileFiltering();
+ void testRefreshFilteredItems();
+ void testCollapseFolderWhileLoading();
+ void testCreateMimeData();
+ void testDeleteFileMoreThanOnce();
private:
QStringList itemsInModel() const;
QSignalSpy itemsInsertedSpy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
- m_testDir->createFiles(QStringList() << "a.txt" << "b.txt" << "c.txt");
+ m_testDir->createFiles({"a.txt", "b.txt", "c.txt"});
m_model->loadDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
QCOMPARE(itemsRemovedSpy.count(), 0);
QCOMPARE(m_model->count(), 3);
- m_testDir->createFiles(QStringList() << "d.txt" << "e.txt");
+ m_testDir->createFiles({"d.txt", "e.txt"});
m_model->m_dirLister->updateDirectory(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
QCOMPARE(loadingCompletedSpy.count(), 2);
// We rename d.txt back to a.txt using the dir lister's refreshItems() signal.
const KFileItem fileItemD = m_model->fileItem(2);
KFileItem fileItemA = fileItemD;
- KUrl urlA = fileItemA.url();
- urlA.setFileName("a.txt");
+ QUrl urlA = fileItemA.url().adjusted(QUrl::RemoveFilename);
+ urlA.setPath(urlA.path() + "a.txt");
fileItemA.setUrl(urlA);
- m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(fileItemD, fileItemA));
+ m_model->slotRefreshItems({qMakePair(fileItemD, fileItemA)});
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
m_testDir->createFiles(files);
// 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");
+ QSet<QUrl> allFolders;
+ allFolders << QUrl::fromLocalFile(m_testDir->name() + 'a')
+ << QUrl::fromLocalFile(m_testDir->name() + "a/a")
+ << QUrl::fromLocalFile(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<QUrl>() << QUrl::fromLocalFile(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<QUrl>() << QUrl::fromLocalFile(m_testDir->name() + 'a') << QUrl::fromLocalFile(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(QUrl::fromLocalFile(m_testDir->name() + 'a'))); // TODO: Make sure that child URLs are also removed
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
// Move to a sub folder, then call restoreExpandedFolders() *before* going back.
// This is how DolphinView restores the expanded folders when navigating in history.
- m_model->loadDirectory(KUrl(m_testDir->name() + "a/a/"));
+ m_model->loadDirectory(QUrl::fromLocalFile(m_testDir->name() + "a/a/"));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
QCOMPARE(m_model->count(), 1); // 1 item: "1"
m_model->restoreExpandedDirectories(allFolders);
m_model->setRoles(originalModelRoles);
QVERIFY(!m_model->isExpanded(0));
QCOMPARE(m_model->count(), 1);
- QVERIFY(!m_model->expandedDirectories().contains(KUrl(m_testDir->name() + 'a')));
+ QVERIFY(!m_model->expandedDirectories().contains(QUrl::fromLocalFile(m_testDir->name() + 'a')));
QCOMPARE(spyRemoved.count(), 1);
itemRangeList = spyRemoved.takeFirst().at(0).value<KItemRangeList>();
QVERIFY(m_model->expandedDirectories().empty());
// Expand the parents of "a2/b2/c2".
- m_model->expandParentDirectories(KUrl(m_testDir->name() + "a2/b2/c2"));
+ m_model->expandParentDirectories(QUrl::fromLocalFile(m_testDir->name() + "a2/b2/c2"));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
// The model should now contain "a 1/", "a2/", "a2/b2/", and "a2/b2/c2/".
QVERIFY(!m_model->isExpanded(3));
// Expand the parents of "a 1/b1".
- m_model->expandParentDirectories(KUrl(m_testDir->name() + "a 1/b1"));
+ m_model->expandParentDirectories(QUrl::fromLocalFile(m_testDir->name() + "a 1/b1"));
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(directoryLoadingCompleted()), DefaultTimeout));
// The model should now contain "a 1/", "a 1/b1/", "a2/", "a2/b2", and "a2/b2/c2/".
QCOMPARE(m_model->count(), 6);
// Rename "1a/2" and make it hidden.
- const QString oldPath = m_model->fileItem(0).url().path() + "/2a";
- const QString newPath = m_model->fileItem(0).url().path() + "/.2a";
+ 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(oldPath, newPath, KIO::HideProgressInfo);
+ KIO::SimpleJob* job = KIO::rename(oldUrl, newUrl, KIO::HideProgressInfo);
bool ok = job->exec();
QVERIFY(ok);
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsRemoved(KItemRangeList)), DefaultTimeout));
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(QUrl(m_testDir->url().url() + 'c'));
m_model->setExpanded(index, true);
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
- index = m_model->index(KUrl(m_testDir->url().url() + "c/c-2"));
+ index = m_model->index(QUrl(m_testDir->url().url() + "c/c-2"));
m_model->setExpanded(index, true);
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
roles.insert("expandedParentsCount");
m_model->setRoles(roles);
- const KUrl emptyUrl;
+ const QUrl emptyUrl;
QVERIFY(emptyUrl.path().isEmpty());
- const KUrl url("file:///test/");
+ const QUrl url("file:///test/");
KFileItemList items;
items << KFileItem(emptyUrl, QString(), KFileItem::Unknown) << KFileItem(url, QString(), KFileItem::Unknown);
QSignalSpy spyItemsChanged(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)));
const KFileItem item = m_model->fileItem(0);
- m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(item, item));
+ m_model->slotRefreshItems({qMakePair(item, item)});
QVERIFY(!spyItemsChanged.isEmpty());
QCOMPARE(m_model->count(), 5); // "a/", "a/1", "a/2", "3", "4"
m_testDir->createDir(".b");
m_testDir->createDir("c");
m_testDir->createDir("d");
- m_testDir->createFiles(QStringList() << ".f" << ".g" << "h" << "i");
+ m_testDir->createFiles({".f", ".g", "h", "i"});
QSignalSpy spyItemsInserted(m_model, SIGNAL(itemsInserted(KItemRangeList)));
QSignalSpy spyItemsRemoved(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2" << "realGrandChild2");
// Add some more children and grand-children.
- const KUrl parent1 = m_model->fileItem(0).url();
- const KUrl parent2 = m_model->fileItem(3).url();
- const KUrl realChild1 = m_model->fileItem(1).url();
- const KUrl realChild2 = m_model->fileItem(4).url();
+ const QUrl parent1 = m_model->fileItem(0).url();
+ const QUrl parent2 = m_model->fileItem(3).url();
+ const QUrl realChild1 = m_model->fileItem(1).url();
+ const QUrl realChild2 = m_model->fileItem(4).url();
- m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(KUrl("child1"), QString(), KFileItem::Unknown));
+ m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(QUrl("child1"), QString(), KFileItem::Unknown));
m_model->slotCompleted();
QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2");
- m_model->slotItemsAdded(parent2, KFileItemList() << KFileItem(KUrl("child2"), QString(), KFileItem::Unknown));
+ m_model->slotItemsAdded(parent2, KFileItemList() << KFileItem(QUrl("child2"), QString(), KFileItem::Unknown));
m_model->slotCompleted();
QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "child1" << "parent2" << "realChild2" << "realGrandChild2" << "child2");
- m_model->slotItemsAdded(realChild1, KFileItemList() << KFileItem(KUrl("grandChild1"), QString(), KFileItem::Unknown));
+ 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(KUrl("grandChild1"), QString(), KFileItem::Unknown));
+ 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(KUrl("grandChild2"), QString(), KFileItem::Unknown));
+ 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");
// Change d.txt back to c.txt, but this time using the dir lister's refreshItems() signal.
const KFileItem fileItemD = m_model->fileItem(2);
KFileItem fileItemC = fileItemD;
- KUrl urlC = fileItemC.url();
- urlC.setFileName("c.txt");
+ QUrl urlC = fileItemC.url().adjusted(QUrl::RemoveFilename);
+ urlC.setPath(urlC.path() + "c.txt");
fileItemC.setUrl(urlC);
- m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(fileItemD, fileItemC));
+ m_model->slotRefreshItems({qMakePair(fileItemD, fileItemC)});
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(groupsChanged()), DefaultTimeout));
QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
// Note that the first item in the list of added items must be new (i.e., not
// in the model yet). Otherwise, KFileItemModel::slotItemsAdded() will see that
// it receives items that are in the model already and ignore them.
- KUrl url(m_model->directory().url() + "/a2");
+ QUrl url(m_model->directory().url() + "/a2");
KFileItem newItem(KFileItem::Unknown, KFileItem::Unknown, url);
KFileItemList items;
}
+void KFileItemModelTest::testChangeRolesForFilteredItems()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "owner";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ files << "a.txt" << "aa.txt" << "aaa.txt";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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".
+ QVERIFY(m_model->data(index).contains("text"));
+ QVERIFY(m_model->data(index).contains("owner"));
+ QVERIFY(!m_model->data(index).contains("group"));
+ }
+
+ // Add a filter, such that only "aaa.txt" remains in the model.
+ m_model->setNameFilter("aaa");
+ QCOMPARE(itemsInModel(), QStringList() << "aaa.txt");
+
+ // Add the "group" role.
+ modelRoles << "group";
+ m_model->setRoles(modelRoles);
+
+ // 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");
+
+ for (int index = 0; index < m_model->count(); ++index) {
+ // All items should have the "text", "owner", and "group" roles.
+ QVERIFY(m_model->data(index).contains("text"));
+ QVERIFY(m_model->data(index).contains("owner"));
+ QVERIFY(m_model->data(index).contains("group"));
+ }
+
+ // Remove the "owner" role.
+ modelRoles.remove("owner");
+ m_model->setRoles(modelRoles);
+
+ // 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");
+
+ for (int index = 0; index < m_model->count(); ++index) {
+ // All items should have the "text" and "group" roles, but now "owner".
+ QVERIFY(m_model->data(index).contains("text"));
+ QVERIFY(!m_model->data(index).contains("owner"));
+ QVERIFY(m_model->data(index).contains("group"));
+ }
+}
+
+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);
+
+ 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.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.insert(KIO::UDSEntry::UDS_NAME, "c.txt");
+ entry.insert(KIO::UDSEntry::UDS_USER, "user-a");
+ items.append(KFileItem(entry, m_testDir->url(), false, true));
+
+ m_model->slotItemsAdded(m_testDir->url(), items);
+ m_model->slotCompleted();
+
+ QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
+
+ // Add a filter.
+ m_model->setNameFilter("a");
+ QCOMPARE(itemsInModel(), QStringList() << "a.txt");
+
+ // Sort by "owner".
+ m_model->setSortRole("owner");
+
+ // Clear the filter, and verify that the items are sorted correctly.
+ m_model->setNameFilter(QString());
+ QCOMPARE(itemsInModel(), QStringList() << "c.txt" << "a.txt" << "b.txt");
+}
+
+void KFileItemModelTest::testRefreshFilteredItems()
+{
+ QStringList files;
+ files << "a.txt" << "b.txt" << "c.jpg" << "d.jpg";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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");
+
+ // Rename one of the .jpg files.
+ KFileItem fileItemE = fileItemC;
+ QUrl urlE = fileItemE.url().adjusted(QUrl::RemoveFilename);
+ urlE.setPath(urlE.path() + "e.jpg");
+ fileItemE.setUrl(urlE);
+
+ m_model->slotRefreshItems({qMakePair(fileItemC, fileItemE)});
+
+ // 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");
+}
+
+void KFileItemModelTest::testCreateMimeData()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ files << "a/1";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "a");
+
+ // Expand "a/".
+ m_model->setExpanded(0, true);
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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);
+ delete mimeData;
+}
+
+void KFileItemModelTest::testCollapseFolderWhileLoading()
+{
+ QSet<QByteArray> modelRoles = m_model->roles();
+ modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+ m_model->setRoles(modelRoles);
+
+ QStringList files;
+ files << "a2/b/c1.txt";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "a2");
+
+ // Expand "a2/".
+ m_model->setExpanded(0, true);
+ QVERIFY(m_model->isExpanded(0));
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "a2" << "b");
+
+ // Expand "a2/b/".
+ m_model->setExpanded(1, true);
+ QVERIFY(m_model->isExpanded(1));
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt");
+
+ // Simulate that a new item "c2.txt" appears, but that the dir lister's completed()
+ // signal is not emitted yet.
+ const KFileItem fileItemC1 = m_model->fileItem(2);
+ KFileItem fileItemC2 = fileItemC1;
+ QUrl urlC2 = fileItemC2.url();
+ urlC2.adjusted(QUrl::RemoveFilename);
+ urlC2.setPath(urlC2.path() + "c2.txt");
+ fileItemC2.setUrl(urlC2);
+
+ const QUrl urlB = m_model->fileItem(1).url();
+ m_model->slotItemsAdded(urlB, KFileItemList() << fileItemC2);
+ QCOMPARE(itemsInModel(), QStringList() << "a2" << "b" << "c1.txt");
+
+ // Collapse "a2/". This should also remove all its (indirect) children from
+ // the model and from the model's m_pendingItemsToInsert member.
+ m_model->setExpanded(0, false);
+ QCOMPARE(itemsInModel(), QStringList() << "a2");
+
+ // Simulate that the dir lister's completed() signal is emitted. If "c2.txt"
+ // is still in m_pendingItemsToInsert, then we might get a crash, see
+ // https://bugs.kde.org/show_bug.cgi?id=332102. Even if the crash is not
+ // reproducible here, Valgrind will complain, and the item "c2.txt" will appear
+ // without parent in the model.
+ m_model->slotCompleted();
+ QCOMPARE(itemsInModel(), QStringList() << "a2");
+
+ // Expand "a2/" again.
+ m_model->setExpanded(0, true);
+ QVERIFY(m_model->isExpanded(0));
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ 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.
+ const KFileItem fileItemA2 = m_model->fileItem(0);
+ KFileItem fileItemA1 = fileItemA2;
+ QUrl urlA1 = fileItemA1.url().adjusted(QUrl::RemoveFilename);
+ urlA1.setPath(urlA1.path() + "a1");
+ fileItemA1.setUrl(urlA1);
+
+ m_model->slotItemsAdded(m_model->directory(), KFileItemList() << fileItemA1);
+ 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");
+ QVERIFY(!m_model->isExpanded(0));
+ QVERIFY(!m_model->isExpanded(1));
+}
+
+void KFileItemModelTest::testDeleteFileMoreThanOnce()
+{
+ QStringList files;
+ files << "a.txt" << "b.txt" << "c.txt" << "d.txt";
+ m_testDir->createFiles(files);
+
+ m_model->loadDirectory(m_testDir->url());
+ QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+ QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "d.txt");
+
+ const KFileItem fileItemB = m_model->fileItem(1);
+
+ // Tell the model that a list of items has been deleted, where "b.txt" appears twice in the list.
+ KFileItemList list;
+ list << fileItemB << fileItemB;
+ m_model->slotItemsDeleted(list);
+
+ QVERIFY(m_model->isConsistent());
+ QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "c.txt" << "d.txt");
+}
+
QStringList KFileItemModelTest::itemsInModel() const
{
QStringList items;