]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Fix possible crash if a kioslave adds multiple items with the same URL
authorFrank Reininghaus <frank78ac@googlemail.com>
Wed, 4 Jun 2014 19:48:19 +0000 (21:48 +0200)
committerFrank Reininghaus <frank78ac@googlemail.com>
Wed, 4 Jun 2014 19:49:02 +0000 (21:49 +0200)
When opening the URL "man:", there are multiple items with the same
name (for example, _exit is shown twice here). When opening a new tab,
the kioslave reports some items as deleted (I have not quite understood
why). The problem is that it reports some of the duplicate items twice
in the list of deleted items. This confused KFileItemModel and
corrupted the internal data structures, and finally, caused a crash.

The fix is to remove all duplicates from
KItemRangeList::fromSortedContainer(const Container& container).

New unit tests included.

BUG: 335672
REVIEW: 118507
FIXED-IN: 4.13.2

src/kitemviews/kfileitemmodel.cpp
src/kitemviews/kitemrange.h
src/tests/CMakeLists.txt
src/tests/kfileitemmodeltest.cpp
src/tests/kitemrangetest.cpp [new file with mode: 0644]

index de3c3eb22e26dacf50fb3ae7c24f6c507d47600d..b3b926c3ac897cb1dc8098e4c86379900ca479b4 100644 (file)
@@ -395,7 +395,41 @@ int KFileItemModel::index(const KUrl& url) const
         index = m_items.value(urlToFind, -1);
     }
 
-    Q_ASSERT(index >= 0 || m_items.count() == m_itemData.count());
+    if (index < 0) {
+        // The item could not be found, even though all items from m_itemData
+        // should be in m_items now. We print some diagnostic information which
+        // might help to find the cause of the problem, but only once. This
+        // prevents that obtaining and printing the debugging information
+        // wastes CPU cycles and floods the shell or .xsession-errors.
+        static bool printDebugInfo = true;
+
+        if (m_items.count() != m_itemData.count() && printDebugInfo) {
+            printDebugInfo = false;
+
+            kWarning() << "The model is in an inconsistent state.";
+            kWarning() << "m_items.count()    ==" << m_items.count();
+            kWarning() << "m_itemData.count() ==" << m_itemData.count();
+
+            // Check if there are multiple items with the same URL.
+            QMultiHash<KUrl, int> indexesForUrl;
+            for (int i = 0; i < m_itemData.count(); ++i) {
+                indexesForUrl.insert(m_itemData.at(i)->item.url(), i);
+            }
+
+            foreach (const KUrl& url, indexesForUrl.uniqueKeys()) {
+                if (indexesForUrl.count(url) > 1) {
+                    kWarning() << "Multiple items found with the URL" << url;
+                    foreach (int index, indexesForUrl.values(url)) {
+                        const ItemData* data = m_itemData.at(index);
+                        kWarning() << "index" << index << ":" << data->item;
+                        if (data->parent) {
+                            kWarning() << "parent" << data->parent->item;
+                        }
+                    }
+                }
+            }
+        }
+    }
 
     return index;
 }
index 70927b91578045d814f9e70d06c5a4df006b79b5..ecc03988d29949474d2436c539588937d2f83649 100644 (file)
@@ -78,7 +78,10 @@ KItemRangeList KItemRangeList::fromSortedContainer(const Container& container)
     int index = *it;
     int count = 1;
 
-    ++it;
+    // Remove duplicates, see https://bugs.kde.org/show_bug.cgi?id=335672
+    while (it != end && *it == index) {
+        ++it;
+    }
 
     while (it != end) {
         if (*it == index + count) {
@@ -89,6 +92,11 @@ KItemRangeList KItemRangeList::fromSortedContainer(const Container& container)
             count = 1;
         }
         ++it;
+
+        // Remove duplicates, see https://bugs.kde.org/show_bug.cgi?id=335672
+        while (it != end && *it == *(it - 1)) {
+            ++it;
+        }
     }
 
     result << KItemRange(index, count);
index 4ba68dd7300f783c3fb1bf594c56e67ab61ec80c..c1f4124ff3e47424be2d1d63bb7dc24f3190f5ef 100644 (file)
@@ -12,6 +12,13 @@ set(kitemsettest_SRCS
 kde4_add_unit_test(kitemsettest TEST ${kitemsettest_SRCS})
 target_link_libraries(kitemsettest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
 
+# KItemRangeTest
+set(kitemrangetest_SRCS
+    kitemrangetest.cpp
+)
+kde4_add_unit_test(kitemrangetest TEST ${kitemrangetest_SRCS})
+target_link_libraries(kitemrangetest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
+
 # KItemListSelectionManagerTest
 set(kitemlistselectionmanagertest_SRCS
     kitemlistselectionmanagertest.cpp
index 48e72e83f3243d699bbcca4b427d7b6fb193fafc..c584c5e62a51e95e2dd25f8808ccc5acbd725611 100644 (file)
@@ -95,6 +95,7 @@ private slots:
     void testRefreshFilteredItems();
     void testCollapseFolderWhileLoading();
     void testCreateMimeData();
+    void testDeleteFileMoreThanOnce();
 
 private:
     QStringList itemsInModel() const;
@@ -1697,6 +1698,27 @@ void KFileItemModelTest::testCollapseFolderWhileLoading()
     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;
diff --git a/src/tests/kitemrangetest.cpp b/src/tests/kitemrangetest.cpp
new file mode 100644 (file)
index 0000000..9f3f799
--- /dev/null
@@ -0,0 +1,75 @@
+/***************************************************************************
+ *   Copyright (C) 2014 by Frank Reininghaus <frank78ac@googlemail.com>    *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA            *
+ ***************************************************************************/
+
+#include <qtest_kde.h>
+
+#include "kitemviews/kitemrange.h"
+
+#include <QVector>
+
+Q_DECLARE_METATYPE(QVector<int>);
+Q_DECLARE_METATYPE(KItemRangeList);
+
+class KItemRangeTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void testFromSortedContainer_data();
+    void testFromSortedContainer();
+};
+
+void KItemRangeTest::testFromSortedContainer_data()
+{
+    QTest::addColumn<QVector<int> >("sortedNumbers");
+    QTest::addColumn<KItemRangeList>("expected");
+
+    QTest::newRow("empty") << QVector<int>() << KItemRangeList();
+    QTest::newRow("[1]") << (QVector<int>() << 1) << (KItemRangeList() << KItemRange(1, 1));
+    QTest::newRow("[9]") << (QVector<int>() << 9) << (KItemRangeList() << KItemRange(9, 1));
+    QTest::newRow("[1-2]") << (QVector<int>() << 1 << 2) << (KItemRangeList() << KItemRange(1, 2));
+    QTest::newRow("[1-3]") << (QVector<int>() << 1 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3));
+    QTest::newRow("[1] [4]") << (QVector<int>() << 1 << 4) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(4, 1));
+    QTest::newRow("[1-3] [5]") << (QVector<int>() << 1 << 2 << 3 << 5) << (KItemRangeList() << KItemRange(1, 3) << KItemRange(5, 1));
+    QTest::newRow("[1] [5-6]") << (QVector<int>() << 1 << 5 << 6) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 2));
+    QTest::newRow("duplicates: 1 1") << (QVector<int>() << 1 << 1) << (KItemRangeList() << KItemRange(1, 1));
+    QTest::newRow("duplicates: 1 1 1") << (QVector<int>() << 1 << 1 << 1) << (KItemRangeList() << KItemRange(1, 1));
+    QTest::newRow("duplicates: 1 1 5") << (QVector<int>() << 1 << 1 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1));
+    QTest::newRow("duplicates: 1 5 5") << (QVector<int>() << 1 << 5 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1));
+    QTest::newRow("duplicates: 1 1 1 5") << (QVector<int>() << 1 << 1 << 1 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1));
+    QTest::newRow("duplicates: 1 5 5 5") << (QVector<int>() << 1 << 5 << 5 << 5) << (KItemRangeList() << KItemRange(1, 1) << KItemRange(5, 1));
+    QTest::newRow("duplicates: 1 1 2") << (QVector<int>() << 1 << 1 << 2) << (KItemRangeList() << KItemRange(1, 2));
+    QTest::newRow("duplicates: 1 2 2") << (QVector<int>() << 1 << 2 << 2) << (KItemRangeList() << KItemRange(1, 2));
+    QTest::newRow("duplicates: 1 1 2 3") << (QVector<int>() << 1 << 1 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3));
+    QTest::newRow("duplicates: 1 2 2 3") << (QVector<int>() << 1 << 2 << 2 << 3) << (KItemRangeList() << KItemRange(1, 3));
+    QTest::newRow("duplicates: 1 2 3 3") << (QVector<int>() << 1 << 2 << 3 << 3) << (KItemRangeList() << KItemRange(1, 3));
+}
+
+void KItemRangeTest::testFromSortedContainer()
+{
+    QFETCH(QVector<int>, sortedNumbers);
+    QFETCH(KItemRangeList, expected);
+
+    const KItemRangeList result = KItemRangeList::fromSortedContainer(sortedNumbers);
+    QCOMPARE(expected, result);
+}
+
+QTEST_KDEMAIN(KItemRangeTest, NoGUI)
+
+#include "kitemrangetest.moc"