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"