File indexing completed on 2024-04-28 05:45:20

0001 /*
0002  * SPDX-FileCopyrightText: 2011 Peter Penz <peter.penz19@gmail.com>
0003  * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-or-later
0006  */
0007 
0008 #include <QSignalSpy>
0009 #include <QStandardPaths>
0010 #include <QTest>
0011 
0012 #include <random>
0013 
0014 #include "kitemviews/kfileitemmodel.h"
0015 #include "kitemviews/private/kfileitemmodelsortalgorithm.h"
0016 
0017 void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
0018 {
0019     Q_UNUSED(context)
0020 
0021     switch (type) {
0022     case QtDebugMsg:
0023         break;
0024     case QtWarningMsg:
0025         break;
0026     case QtCriticalMsg:
0027         fprintf(stderr, "Critical: %s\n", msg.toLocal8Bit().data());
0028         break;
0029     case QtFatalMsg:
0030         fprintf(stderr, "Fatal: %s\n", msg.toLocal8Bit().data());
0031         abort();
0032     default:
0033         break;
0034     }
0035 }
0036 
0037 Q_DECLARE_METATYPE(KFileItemList)
0038 Q_DECLARE_METATYPE(KItemRangeList)
0039 
0040 class KFileItemModelBenchmark : public QObject
0041 {
0042     Q_OBJECT
0043 
0044 public:
0045     KFileItemModelBenchmark();
0046 
0047 private Q_SLOTS:
0048     void initTestCase();
0049     void insertAndRemoveManyItems_data();
0050     void insertAndRemoveManyItems();
0051 
0052 private:
0053     static KFileItemList createFileItemList(const QStringList &fileNames, const QString &urlPrefix = QLatin1String("file:///"));
0054 };
0055 
0056 KFileItemModelBenchmark::KFileItemModelBenchmark()
0057 {
0058 }
0059 
0060 void KFileItemModelBenchmark::initTestCase()
0061 {
0062     QStandardPaths::setTestModeEnabled(true);
0063 }
0064 
0065 void KFileItemModelBenchmark::insertAndRemoveManyItems_data()
0066 {
0067     QTest::addColumn<KFileItemList>("initialItems");
0068     QTest::addColumn<KFileItemList>("newItems");
0069     QTest::addColumn<KFileItemList>("removedItems");
0070     QTest::addColumn<KFileItemList>("expectedFinalItems");
0071     QTest::addColumn<KItemRangeList>("expectedItemsInserted");
0072     QTest::addColumn<KItemRangeList>("expectedItemsRemoved");
0073 
0074     QList<int> sizes;
0075     sizes << 100000;
0076 
0077     for (int n : std::as_const(sizes)) {
0078         QStringList allStrings;
0079         for (int i = 0; i < n; ++i) {
0080             allStrings << QString::number(i);
0081         }
0082 
0083         // We want to keep the sorting overhead in the benchmark low.
0084         // Therefore, we do not use natural sorting. However, this
0085         // means that our list is currently not sorted.
0086         allStrings.sort();
0087 
0088         KFileItemList all = createFileItemList(allStrings);
0089 
0090         KFileItemList firstHalf, secondHalf, even, odd;
0091         for (int i = 0; i < n; ++i) {
0092             if (i < n / 2) {
0093                 firstHalf << all.at(i);
0094             } else {
0095                 secondHalf << all.at(i);
0096             }
0097 
0098             if (i % 2 == 0) {
0099                 even << all.at(i);
0100             } else {
0101                 odd << all.at(i);
0102             }
0103         }
0104 
0105         KItemRangeList itemRangeListFirstHalf;
0106         itemRangeListFirstHalf << KItemRange(0, firstHalf.count());
0107 
0108         KItemRangeList itemRangeListSecondHalf;
0109         itemRangeListSecondHalf << KItemRange(firstHalf.count(), secondHalf.count());
0110 
0111         KItemRangeList itemRangeListOddInserted, itemRangeListOddRemoved;
0112         for (int i = 0; i < odd.count(); ++i) {
0113             // Note that the index in the KItemRange is the index of
0114             // the model *before* the items have been inserted.
0115             itemRangeListOddInserted << KItemRange(i + 1, 1);
0116             itemRangeListOddRemoved << KItemRange(2 * i + 1, 1);
0117         }
0118 
0119         const int bufferSize = 128;
0120         char buffer[bufferSize];
0121 
0122         snprintf(buffer, bufferSize, "all--n=%i", n);
0123         QTest::newRow(buffer) << all << KFileItemList() << KFileItemList() << all << KItemRangeList() << KItemRangeList();
0124 
0125         snprintf(buffer, bufferSize, "1st half + 2nd half--n=%i", n);
0126         QTest::newRow(buffer) << firstHalf << secondHalf << KFileItemList() << all << itemRangeListSecondHalf << KItemRangeList();
0127 
0128         snprintf(buffer, bufferSize, "2nd half + 1st half--n=%i", n);
0129         QTest::newRow(buffer) << secondHalf << firstHalf << KFileItemList() << all << itemRangeListFirstHalf << KItemRangeList();
0130 
0131         snprintf(buffer, bufferSize, "even + odd--n=%i", n);
0132         QTest::newRow(buffer) << even << odd << KFileItemList() << all << itemRangeListOddInserted << KItemRangeList();
0133 
0134         snprintf(buffer, bufferSize, "all - 2nd half--n=%i", n);
0135         QTest::newRow(buffer) << all << KFileItemList() << secondHalf << firstHalf << KItemRangeList() << itemRangeListSecondHalf;
0136 
0137         snprintf(buffer, bufferSize, "all - 1st half--n=%i", n);
0138         QTest::newRow(buffer) << all << KFileItemList() << firstHalf << secondHalf << KItemRangeList() << itemRangeListFirstHalf;
0139 
0140         snprintf(buffer, bufferSize, "all - odd--n=%i", n);
0141         QTest::newRow(buffer) << all << KFileItemList() << odd << even << KItemRangeList() << itemRangeListOddRemoved;
0142     }
0143 }
0144 
0145 void KFileItemModelBenchmark::insertAndRemoveManyItems()
0146 {
0147     QFETCH(KFileItemList, initialItems);
0148     QFETCH(KFileItemList, newItems);
0149     QFETCH(KFileItemList, removedItems);
0150     QFETCH(KFileItemList, expectedFinalItems);
0151     QFETCH(KItemRangeList, expectedItemsInserted);
0152     QFETCH(KItemRangeList, expectedItemsRemoved);
0153 
0154     KFileItemModel model;
0155 
0156     // Avoid overhead caused by natural sorting
0157     // and determining the isDir/isLink roles.
0158     model.m_naturalSorting = false;
0159     model.setRoles({"text"});
0160 
0161     QSignalSpy spyItemsInserted(&model, &KFileItemModel::itemsInserted);
0162     QSignalSpy spyItemsRemoved(&model, &KFileItemModel::itemsRemoved);
0163 
0164     QBENCHMARK {
0165         model.slotClear();
0166         model.slotItemsAdded(model.directory(), initialItems);
0167         model.slotCompleted();
0168         QCOMPARE(model.count(), initialItems.count());
0169 
0170         if (!newItems.isEmpty()) {
0171             model.slotItemsAdded(model.directory(), newItems);
0172             model.slotCompleted();
0173         }
0174         QCOMPARE(model.count(), initialItems.count() + newItems.count());
0175 
0176         if (!removedItems.isEmpty()) {
0177             model.slotItemsDeleted(removedItems);
0178         }
0179         QCOMPARE(model.count(), initialItems.count() + newItems.count() - removedItems.count());
0180     }
0181 
0182     QVERIFY(model.isConsistent());
0183 
0184     for (int i = 0; i < model.count(); ++i) {
0185         QCOMPARE(model.fileItem(i), expectedFinalItems.at(i));
0186     }
0187 
0188     if (!expectedItemsInserted.empty()) {
0189         QVERIFY(!spyItemsInserted.empty());
0190         const KItemRangeList actualItemsInserted = spyItemsInserted.last().first().value<KItemRangeList>();
0191         QCOMPARE(actualItemsInserted, expectedItemsInserted);
0192     }
0193 
0194     if (!expectedItemsRemoved.empty()) {
0195         QVERIFY(!spyItemsRemoved.empty());
0196         const KItemRangeList actualItemsRemoved = spyItemsRemoved.last().first().value<KItemRangeList>();
0197         QCOMPARE(actualItemsRemoved, expectedItemsRemoved);
0198     }
0199 }
0200 
0201 KFileItemList KFileItemModelBenchmark::createFileItemList(const QStringList &fileNames, const QString &prefix)
0202 {
0203     // Suppress 'file does not exist anymore' messages from KFileItemPrivate::init().
0204     qInstallMessageHandler(myMessageOutput);
0205 
0206     KFileItemList result;
0207     for (const QString &name : fileNames) {
0208         const KFileItem item(QUrl::fromLocalFile(prefix + name), QString(), KFileItem::Unknown);
0209         result << item;
0210     }
0211     return result;
0212 }
0213 
0214 QTEST_MAIN(KFileItemModelBenchmark)
0215 
0216 #include "kfileitemmodelbenchmark.moc"