]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/tests/kfileitemmodeltest.cpp
Include "Space" in the keyboard search string
[dolphin.git] / src / tests / kfileitemmodeltest.cpp
index 1145e1535dd6fc4fbeb3d189913f602bebb24ada..5dd3417fc5c43225c58328e4b58ebbe987f40382 100644 (file)
@@ -69,17 +69,25 @@ private slots:
     void testSetData();
     void testSetDataWithModifiedSortRole_data();
     void testSetDataWithModifiedSortRole();
+    void testChangeSortRole();
+    void testResortAfterChangingName();
     void testModelConsistencyWhenInsertingItems();
     void testItemRangeConsistencyWhenInsertingItems();
     void testExpandItems();
     void testExpandParentItems();
     void testMakeExpandedItemHidden();
+    void testRemoveFilteredExpandedItems();
     void testSorting();
     void testIndexForKeyboardSearch();
     void testNameFilter();
     void testEmptyPath();
+    void testRefreshExpandedItem();
     void testRemoveHiddenItems();
+    void collapseParentOfHiddenItems();
     void removeParentOfHiddenItems();
+    void testGeneralParentChildRelationships();
+    void testNameRoleGroups();
+    void testNameRoleGroupsWithExpandedItems();
 
 private:
     QStringList itemsInModel() const;
@@ -102,6 +110,9 @@ void KFileItemModelTest::init()
     m_testDir = new TestDir();
     m_model = new KFileItemModel();
     m_model->m_dirLister->setAutoUpdate(false);
+
+    // Reduce the timer interval to make the test run faster.
+    m_model->m_resortAllItemsTimer->setInterval(0);
 }
 
 void KFileItemModelTest::cleanup()
@@ -274,6 +285,8 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
     // Changing the value of a sort-role must result in
     // a reordering of the items.
     QCOMPARE(m_model->sortRole(), QByteArray("text"));
+    m_model->setSortRole("rating");
+    QCOMPARE(m_model->sortRole(), QByteArray("rating"));
 
     QStringList files;
     files << "a.txt" << "b.txt" << "c.txt";
@@ -299,7 +312,6 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
     ratingC.insert("rating", 6);
     m_model->setData(2, ratingC);
 
-    m_model->setSortRole("rating");
     QCOMPARE(m_model->data(0).value("rating").toInt(), 2);
     QCOMPARE(m_model->data(1).value("rating").toInt(), 4);
     QCOMPARE(m_model->data(2).value("rating").toInt(), 6);
@@ -320,6 +332,81 @@ void KFileItemModelTest::testSetDataWithModifiedSortRole()
     QVERIFY(m_model->isConsistent());
 }
 
+void KFileItemModelTest::testChangeSortRole()
+{
+    QCOMPARE(m_model->sortRole(), QByteArray("text"));
+
+    QStringList files;
+    files << "a.txt" << "b.jpg" << "c.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.jpg" << "c.txt");
+
+    // Simulate that KFileItemModelRolesUpdater determines the mime type.
+    // Resorting the files by 'type' will only work immediately if their
+    // mime types are known.
+    for (int index = 0; index < m_model->count(); ++index) {
+        m_model->fileItem(index).determineMimeType();
+    }
+
+    // Now: sort by type.
+    QSignalSpy spyItemsMoved(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)));
+    m_model->setSortRole("type");
+    QCOMPARE(m_model->sortRole(), QByteArray("type"));
+    QVERIFY(!spyItemsMoved.isEmpty());
+
+    // 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";
+
+    QStringList version2;
+    version2 << "a.txt" << "c.txt" << "b.jpg";
+
+    const bool ok1 = (itemsInModel() == version1);
+    const bool ok2 = (itemsInModel() == version2);
+
+    QVERIFY(ok1 || ok2);
+}
+
+void KFileItemModelTest::testResortAfterChangingName()
+{
+    // We sort by size in a directory where all files have the same size.
+    // Therefore, the files are sorted by their names.
+    m_model->setSortRole("size");
+
+    QStringList files;
+    files << "a.txt" << "b.txt" << "c.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");
+
+    // We rename a.txt to d.txt. Even though the size has not changed at all,
+    // the model must re-sort the items.
+    QHash<QByteArray, QVariant> data;
+    data.insert("text", "d.txt");
+    m_model->setData(0, data);
+
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    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);
+    KFileItem fileItemA = fileItemD;
+    KUrl urlA = fileItemA.url();
+    urlA.setFileName("a.txt");
+    fileItemA.setUrl(urlA);
+
+    m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(fileItemD, fileItemA));
+
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt");
+}
+
 void KFileItemModelTest::testModelConsistencyWhenInsertingItems()
 {
     //QSKIP("Temporary disabled", SkipSingle);
@@ -622,6 +709,51 @@ void KFileItemModelTest::testMakeExpandedItemHidden()
 
 }
 
+void KFileItemModelTest::testRemoveFilteredExpandedItems()
+{
+    QSet<QByteArray> originalModelRoles = m_model->roles();
+    QSet<QByteArray> modelRoles = originalModelRoles;
+    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+    m_model->setRoles(modelRoles);
+
+    QStringList files;
+    files << "folder/child" << "file"; // missing folders are created automatically
+    m_testDir->createFiles(files);
+
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+
+    // So far, the model contains only "folder/" and "file".
+    QCOMPARE(m_model->count(), 2);
+    QVERIFY(m_model->isExpandable(0));
+    QVERIFY(!m_model->isExpandable(1));
+    QVERIFY(!m_model->isExpanded(0));
+    QVERIFY(!m_model->isExpanded(1));
+    QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+
+    // Expand "folder" -> "folder/child" becomes visible.
+    m_model->setExpanded(0, true);
+    QVERIFY(m_model->isExpanded(0));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "folder" << "child" << "file");
+
+    // Add a name filter.
+    m_model->setNameFilter("f");
+    QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+
+    m_model->setNameFilter("fo");
+    QCOMPARE(itemsInModel(), QStringList() << "folder");
+
+    // Remove all expanded items by changing the roles
+    m_model->setRoles(originalModelRoles);
+    QVERIFY(!m_model->isExpanded(0));
+    QCOMPARE(itemsInModel(), QStringList() << "folder");
+
+    // Remove the name filter and verify that "folder/child" does not reappear.
+    m_model->setNameFilter(QString());
+    QCOMPARE(itemsInModel(), QStringList() << "folder" << "file");
+}
+
 void KFileItemModelTest::testSorting()
 {
     // Create some files with different sizes and modification times to check the different sorting options
@@ -845,15 +977,48 @@ void KFileItemModelTest::testEmptyPath()
 
     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->slotItemsAdded(emptyUrl, items);
     m_model->slotCompleted();
 }
 
+/**
+ * Verifies that the 'isExpanded' state of folders does not change when the
+ * 'refreshItems' signal is received, see https://bugs.kde.org/show_bug.cgi?id=299675.
+ */
+void KFileItemModelTest::testRefreshExpandedItem()
+{
+    QSet<QByteArray> modelRoles = m_model->roles();
+    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+    m_model->setRoles(modelRoles);
+
+    QStringList files;
+    files << "a/1" << "a/2" << "3" << "4";
+    m_testDir->createFiles(files);
+
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(m_model->count(), 3); // "a/", "3", "4"
+
+    m_model->setExpanded(0, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(m_model->count(), 5); // "a/", "a/1", "a/2", "3", "4"
+    QVERIFY(m_model->isExpanded(0));
+
+    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));
+    QVERIFY(!spyItemsChanged.isEmpty());
+
+    QCOMPARE(m_model->count(), 5); // "a/", "a/1", "a/2", "3", "4"
+    QVERIFY(m_model->isExpanded(0));
+}
+
 /**
  * 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
@@ -904,6 +1069,55 @@ void KFileItemModelTest::testRemoveHiddenItems()
     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.
  */
@@ -954,11 +1168,207 @@ void KFileItemModelTest::removeParentOfHiddenItems()
     QCOMPARE(itemsInModel(), QStringList() << "a" << "1");
 }
 
+/**
+ * Create a tree structure where parent-child relationships can not be
+ * determined by parsing the URLs, and verify that KFileItemModel
+ * handles them correctly.
+ */
+void KFileItemModelTest::testGeneralParentChildRelationships()
+{
+    QSet<QByteArray> modelRoles = m_model->roles();
+    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+    m_model->setRoles(modelRoles);
+
+    QStringList files;
+    files << "parent1/realChild1/realGrandChild1" << "parent2/realChild2/realGrandChild2";
+    m_testDir->createFiles(files);
+
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2");
+
+    // Expand all folders.
+    m_model->setExpanded(0, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2");
+
+    m_model->setExpanded(1, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2");
+
+    m_model->setExpanded(3, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "realGrandChild1" << "parent2" << "realChild2");
+
+    m_model->setExpanded(4, true);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    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();
+
+    m_model->slotItemsAdded(parent1, KFileItemList() << KFileItem(KUrl("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->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->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->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->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.
+    QSignalSpy itemsRemovedSpy(m_model, SIGNAL(itemsRemoved(KItemRangeList)));
+    m_model->setNameFilter("xyz");
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "realChild1" << "parent2" << "realChild2");
+    QCOMPARE(itemsRemovedSpy.count(), 1);
+    QList<QVariant> arguments = itemsRemovedSpy.takeFirst();
+    KItemRangeList itemRangeList = arguments.at(0).value<KItemRangeList>();
+    QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(2, 3) << KItemRange(7, 3));
+
+    // Collapse "parent1".
+    m_model->setExpanded(0, false);
+    QCOMPARE(itemsInModel(), QStringList() << "parent1" << "parent2" << "realChild2");
+    QCOMPARE(itemsRemovedSpy.count(), 1);
+    arguments = itemsRemovedSpy.takeFirst();
+    itemRangeList = arguments.at(0).value<KItemRangeList>();
+    QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 1));
+
+    // Remove "parent2".
+    m_model->slotItemsDeleted(KFileItemList() << m_model->fileItem(1));
+    QCOMPARE(itemsInModel(), QStringList() << "parent1");
+    QCOMPARE(itemsRemovedSpy.count(), 1);
+    arguments = itemsRemovedSpy.takeFirst();
+    itemRangeList = arguments.at(0).value<KItemRangeList>();
+    QCOMPARE(itemRangeList, KItemRangeList() << KItemRange(1, 2));
+
+    // Clear filter, verify that no items reappear.
+    m_model->setNameFilter(QString());
+    QCOMPARE(itemsInModel(), QStringList() << "parent1");
+}
+
+void KFileItemModelTest::testNameRoleGroups()
+{
+    QStringList files;
+    files << "b.txt" << "c.txt" << "d.txt" << "e.txt";
+
+    m_testDir->createFiles(files);
+
+    m_model->setGroupedSorting(true);
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "b.txt" << "c.txt" << "d.txt" << "e.txt");
+
+    QList<QPair<int, QVariant> > expectedGroups;
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("B"));
+    expectedGroups << QPair<int, QVariant>(1, QLatin1String("C"));
+    expectedGroups << QPair<int, QVariant>(2, QLatin1String("D"));
+    expectedGroups << QPair<int, QVariant>(3, QLatin1String("E"));
+    QCOMPARE(m_model->groups(), expectedGroups);
+
+    // Rename d.txt to a.txt.
+    QHash<QByteArray, QVariant> data;
+    data.insert("text", "a.txt");
+    m_model->setData(2, data);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
+
+    expectedGroups.clear();
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
+    expectedGroups << QPair<int, QVariant>(1, QLatin1String("B"));
+    expectedGroups << QPair<int, QVariant>(2, QLatin1String("C"));
+    expectedGroups << QPair<int, QVariant>(3, QLatin1String("E"));
+    QCOMPARE(m_model->groups(), expectedGroups);
+
+    // Rename c.txt to d.txt.
+    data.insert("text", "d.txt");
+    m_model->setData(2, data);
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "d.txt" << "e.txt");
+
+    expectedGroups.clear();
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
+    expectedGroups << QPair<int, QVariant>(1, QLatin1String("B"));
+    expectedGroups << QPair<int, QVariant>(2, QLatin1String("D"));
+    expectedGroups << QPair<int, QVariant>(3, QLatin1String("E"));
+    QCOMPARE(m_model->groups(), expectedGroups);
+
+    // 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");
+    fileItemC.setUrl(urlC);
+
+    m_model->slotRefreshItems(QList<QPair<KFileItem, KFileItem> >() << qMakePair(fileItemD, fileItemC));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsMoved(KItemRange,QList<int>)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a.txt" << "b.txt" << "c.txt" << "e.txt");
+
+    expectedGroups.clear();
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
+    expectedGroups << QPair<int, QVariant>(1, QLatin1String("B"));
+    expectedGroups << QPair<int, QVariant>(2, QLatin1String("C"));
+    expectedGroups << QPair<int, QVariant>(3, QLatin1String("E"));
+    QCOMPARE(m_model->groups(), expectedGroups);
+}
+
+void KFileItemModelTest::testNameRoleGroupsWithExpandedItems()
+{
+    QSet<QByteArray> modelRoles = m_model->roles();
+    modelRoles << "isExpanded" << "isExpandable" << "expandedParentsCount";
+    m_model->setRoles(modelRoles);
+
+    QStringList files;
+    files << "a/b.txt" << "a/c.txt" << "d/e.txt" << "d/f.txt";
+
+    m_testDir->createFiles(files);
+
+    m_model->setGroupedSorting(true);
+    m_model->loadDirectory(m_testDir->url());
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "d");
+
+    QList<QPair<int, QVariant> > expectedGroups;
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
+    expectedGroups << QPair<int, QVariant>(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").
+    expectedGroups.clear();
+    expectedGroups << QPair<int, QVariant>(0, QLatin1String("A"));
+    expectedGroups << QPair<int, QVariant>(3, QLatin1String("D"));
+
+    m_model->setExpanded(0, true);
+    QVERIFY(m_model->isExpanded(0));
+    QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    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(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
+    QCOMPARE(itemsInModel(), QStringList() << "a" << "b.txt" << "c.txt" << "d" << "e.txt" << "f.txt");
+    QCOMPARE(m_model->groups(), expectedGroups);
+}
+
 QStringList KFileItemModelTest::itemsInModel() const
 {
     QStringList items;
     for (int i = 0; i < m_model->count(); i++) {
-        items << m_model->data(i).value("text").toString();
+        items << m_model->fileItem(i).text();
     }
     return items;
 }