File indexing completed on 2024-12-01 06:44:46
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kdirlistertest.h" 0009 0010 #include "jobuidelegatefactory.h" 0011 #include "kiotesthelper.h" 0012 #include <kio/copyjob.h> 0013 #include <kio/deletejob.h> 0014 #include <kio/jobuidelegateextension.h> 0015 #include <kio/simplejob.h> 0016 #include <kprotocolinfo.h> 0017 0018 #include <KDirNotify> 0019 #include <KDirWatch> 0020 0021 #include <QDebug> 0022 #include <QTemporaryFile> 0023 #include <QTest> 0024 0025 QTEST_MAIN(KDirListerTest) 0026 0027 static constexpr bool s_workaroundBrokenInotify = false; 0028 0029 GlobalInits::GlobalInits() 0030 { 0031 // Must be done before the QSignalSpys connect 0032 qRegisterMetaType<KFileItem>(); 0033 qRegisterMetaType<KFileItemList>(); 0034 qRegisterMetaType<KIO::Job *>(); 0035 } 0036 0037 QString KDirListerTest::tempPath() const 0038 { 0039 return m_tempDir->path() + '/'; 0040 } 0041 0042 void KDirListerTest::initTestCase() 0043 { 0044 // To avoid failing on broken locally defined MIME types 0045 QStandardPaths::setTestModeEnabled(true); 0046 0047 m_tempDir.reset(new QTemporaryDir(homeTmpDir())); 0048 0049 // No message dialogs 0050 KIO::setDefaultJobUiDelegateExtension(nullptr); 0051 KIO::setDefaultJobUiDelegateFactory(nullptr); 0052 0053 m_exitCount = 1; 0054 0055 s_referenceTimeStamp = QDateTime::currentDateTime().addSecs(-120); // 2 minutes ago 0056 0057 // Create test data: 0058 /* 0059 * PATH/toplevelfile_1 0060 * PATH/toplevelfile_2 0061 * PATH/toplevelfile_3 0062 * PATH/subdir 0063 * PATH/subdir/testfile 0064 * PATH/subdir/subsubdir 0065 * PATH/subdir/subsubdir/testfile 0066 */ 0067 createTestFile(tempPath() + "toplevelfile_1"); 0068 createTestFile(tempPath() + "toplevelfile_2"); 0069 createTestFile(tempPath() + "toplevelfile_3"); 0070 createTestDirectory(tempPath() + "subdir"); 0071 createTestDirectory(tempPath() + "subdir/subsubdir"); 0072 0073 qRegisterMetaType<QList<QPair<KFileItem, KFileItem>>>(); 0074 } 0075 0076 void KDirListerTest::cleanup() 0077 { 0078 m_dirLister.clearSpies(); 0079 disconnect(&m_dirLister, nullptr, this, nullptr); 0080 } 0081 0082 void KDirListerTest::testInvalidUrl() 0083 { 0084 m_dirLister.openUrl(QUrl(":/"), KDirLister::NoFlags); 0085 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0086 QVERIFY(m_dirLister.spyJobError.wait()); 0087 QCOMPARE(m_dirLister.spyJobError.at(0).at(0).value<KIO::Job *>()->error(), KIO::ERR_MALFORMED_URL); 0088 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0089 QVERIFY(m_dirLister.isFinished()); 0090 } 0091 0092 void KDirListerTest::testNonListableUrl() 0093 { 0094 m_dirLister.openUrl(QUrl("data:foo"), KDirLister::NoFlags); 0095 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0096 QVERIFY(m_dirLister.spyJobError.wait()); 0097 QCOMPARE(m_dirLister.spyJobError.at(0).at(0).value<KIO::Job *>()->error(), KIO::ERR_UNSUPPORTED_ACTION); 0098 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0099 QVERIFY(m_dirLister.isFinished()); 0100 } 0101 0102 void KDirListerTest::testOpenUrl() 0103 { 0104 m_items.clear(); 0105 const QString path = tempPath(); 0106 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0107 // The call to openUrl itself, emits started 0108 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0109 0110 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0111 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0112 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 0113 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0114 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0115 QCOMPARE(m_dirLister.spyClear.count(), 1); 0116 0117 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0118 0119 QCOMPARE(m_dirLister.spyRedirection.count(), 0); 0120 QCOMPARE(m_items.count(), 0); 0121 QVERIFY(!m_dirLister.isFinished()); 0122 0123 // then wait for completed 0124 qDebug("waiting for completed"); 0125 QTRY_COMPARE(m_dirLister.spyStarted.count(), 1); 0126 QTRY_COMPARE(m_dirLister.spyCompleted.count(), 1); 0127 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0128 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0129 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0130 QCOMPARE(m_dirLister.spyClear.count(), 1); 0131 0132 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0133 0134 QCOMPARE(m_dirLister.spyRedirection.count(), 0); 0135 // qDebug() << m_items; 0136 // qDebug() << "In dir" << QDir(path).entryList( QDir::AllEntries | QDir::NoDotAndDotDot); 0137 QCOMPARE(m_items.count(), fileCount()); 0138 QVERIFY(m_dirLister.isFinished()); 0139 disconnect(&m_dirLister, nullptr, this, nullptr); 0140 0141 const QString fileName = QStringLiteral("toplevelfile_3"); 0142 const QUrl itemUrl = QUrl::fromLocalFile(path + fileName); 0143 KFileItem byName = m_dirLister.findByName(fileName); 0144 QVERIFY(!byName.isNull()); 0145 QCOMPARE(byName.url().toString(), itemUrl.toString()); 0146 QCOMPARE(byName.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0147 KFileItem byUrl = m_dirLister.findByUrl(itemUrl); 0148 QVERIFY(!byUrl.isNull()); 0149 QCOMPARE(byUrl.url().toString(), itemUrl.toString()); 0150 QCOMPARE(byUrl.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0151 KFileItem itemForUrl = KDirLister::cachedItemForUrl(itemUrl); 0152 QVERIFY(!itemForUrl.isNull()); 0153 QCOMPARE(itemForUrl.url().toString(), itemUrl.toString()); 0154 QCOMPARE(itemForUrl.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0155 0156 KFileItem rootByUrl = m_dirLister.findByUrl(QUrl::fromLocalFile(path)); 0157 QVERIFY(!rootByUrl.isNull()); 0158 QCOMPARE(QString(rootByUrl.url().toLocalFile() + '/'), path); 0159 0160 m_dirLister.clearSpies(); // for the tests that call testOpenUrl for setup 0161 } 0162 0163 // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. 0164 void KDirListerTest::testOpenUrlFromCache() 0165 { 0166 // Do the same again, it should behave the same, even with the items in the cache 0167 testOpenUrl(); 0168 0169 // Get into the case where another dirlister is holding the items 0170 { 0171 m_items.clear(); 0172 const QString path = tempPath(); 0173 MyDirLister secondDirLister; 0174 connect(&secondDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0175 0176 secondDirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0177 QCOMPARE(secondDirLister.spyStarted.count(), 1); 0178 QCOMPARE(secondDirLister.spyCompleted.count(), 0); 0179 QCOMPARE(secondDirLister.spyCompletedQUrl.count(), 0); 0180 QCOMPARE(secondDirLister.spyCanceled.count(), 0); 0181 QCOMPARE(secondDirLister.spyCanceledQUrl.count(), 0); 0182 QCOMPARE(secondDirLister.spyClear.count(), 1); 0183 0184 QCOMPARE(secondDirLister.spyClearDir.count(), 0); 0185 0186 QCOMPARE(m_items.count(), 0); 0187 QVERIFY(!secondDirLister.isFinished()); 0188 0189 // then wait for completed 0190 qDebug("waiting for completed"); 0191 QTRY_COMPARE(secondDirLister.spyStarted.count(), 1); 0192 QTRY_COMPARE(secondDirLister.spyCompleted.count(), 1); 0193 QCOMPARE(secondDirLister.spyCompletedQUrl.count(), 1); 0194 QCOMPARE(secondDirLister.spyCanceled.count(), 0); 0195 QCOMPARE(secondDirLister.spyCanceledQUrl.count(), 0); 0196 QCOMPARE(secondDirLister.spyClear.count(), 1); 0197 0198 QCOMPARE(secondDirLister.spyClearDir.count(), 0); 0199 0200 QCOMPARE(m_items.count(), 4); 0201 QVERIFY(secondDirLister.isFinished()); 0202 } 0203 0204 disconnect(&m_dirLister, nullptr, this, nullptr); 0205 } 0206 0207 // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. 0208 // This test creates 1 file in the temporary directory 0209 void KDirListerTest::testNewItem() 0210 { 0211 QCOMPARE(m_items.count(), 4); 0212 const QString path = tempPath(); 0213 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0214 0215 qDebug() << "Creating a new file"; 0216 const QString fileName = QStringLiteral("toplevelfile_new"); 0217 0218 createSimpleFile(path + fileName); 0219 0220 QTRY_COMPARE(m_items.count(), 5); 0221 QCOMPARE(m_dirLister.spyStarted.count(), 1); // Updates call started 0222 QCOMPARE(m_dirLister.spyCompleted.count(), 1); // and completed 0223 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0224 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0225 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0226 QCOMPARE(m_dirLister.spyClear.count(), 0); 0227 0228 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0229 0230 const QUrl itemUrl = QUrl::fromLocalFile(path + fileName); 0231 KFileItem itemForUrl = KDirLister::cachedItemForUrl(itemUrl); 0232 QVERIFY(!itemForUrl.isNull()); 0233 QCOMPARE(itemForUrl.url().toString(), itemUrl.toString()); 0234 QCOMPARE(itemForUrl.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0235 disconnect(&m_dirLister, nullptr, this, nullptr); 0236 } 0237 0238 // This test assumes testNewItem was run before. So m_dirLister is holding the items already. 0239 // This test creates 100 more files in the temporary directory in reverse order 0240 void KDirListerTest::testNewItems() 0241 { 0242 QCOMPARE(m_items.count(), 5); 0243 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0244 0245 const QString path = tempPath(); 0246 0247 qDebug() << "Creating 100 new files"; 0248 for (int i = 50; i > 0; i--) { 0249 createSimpleFile(path + QString("toplevelfile_new_%1").arg(i)); 0250 } 0251 QTest::qWait(1000); // Create them with 1s difference 0252 for (int i = 100; i > 50; i--) { 0253 createSimpleFile(path + QString("toplevelfile_new_%1").arg(i)); 0254 } 0255 0256 // choose one of the new created files 0257 const QString fileName = QStringLiteral("toplevelfile_new_50"); 0258 0259 QTRY_COMPARE(m_items.count(), 105); 0260 0261 QVERIFY(m_dirLister.spyStarted.count() > 0 && m_dirLister.spyStarted.count() < 3); // Updates call started, probably twice 0262 QVERIFY(m_dirLister.spyCompleted.count() > 0 && m_dirLister.spyCompleted.count() < 3); // and completed, probably twice 0263 QVERIFY(m_dirLister.spyCompletedQUrl.count() < 3); 0264 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0265 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0266 QCOMPARE(m_dirLister.spyClear.count(), 0); 0267 0268 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0269 0270 const QUrl itemUrl = QUrl::fromLocalFile(path + fileName); 0271 KFileItem itemForUrl = KDirLister::cachedItemForUrl(itemUrl); 0272 QVERIFY(!itemForUrl.isNull()); 0273 QCOMPARE(itemForUrl.url().toString(), itemUrl.toString()); 0274 QCOMPARE(itemForUrl.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0275 } 0276 0277 void KDirListerTest::benchFindByUrl() 0278 { 0279 // We don't want to run benchmarks as part of the normal tests. This test depends on things being set up, which makes moving it to it's own file hard 0280 QSKIP("Skipped by default"); 0281 // The time used should be in the order of O(100*log2(100)) 0282 const QString path = tempPath(); 0283 QBENCHMARK { 0284 for (int i = 100; i > 0; i--) { 0285 KFileItem cachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(path + QString("toplevelfile_new_%1").arg(i))); 0286 QVERIFY(!cachedItem.isNull()); 0287 } 0288 } 0289 } 0290 0291 void KDirListerTest::testNewItemByCopy() 0292 { 0293 // This test creates a file using KIO::copyAs, like knewmenu.cpp does. 0294 // Useful for testing #192185, i.e. whether we catch the kdirwatch event and avoid 0295 // a KFileItem::refresh(). 0296 const int origItemCount = m_items.count(); 0297 const QString path = tempPath(); 0298 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0299 0300 QTest::qWait(1000); // We need a 1s timestamp difference on the dir, otherwise FAM won't notice anything. 0301 0302 const QString fileName = QStringLiteral("toplevelfile_copy"); 0303 const QUrl itemUrl = QUrl::fromLocalFile(path + fileName); 0304 KIO::CopyJob *job = KIO::copyAs(QUrl::fromLocalFile(path + "toplevelfile_3"), itemUrl, KIO::HideProgressInfo); 0305 job->exec(); 0306 0307 // Give time for KDirWatch/KDirNotify to notify us 0308 QTRY_COMPARE(m_items.count(), origItemCount + 1); 0309 0310 QCOMPARE(m_dirLister.spyStarted.count(), 1); // Updates call started 0311 QCOMPARE(m_dirLister.spyCompleted.count(), 1); // and completed 0312 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0313 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0314 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0315 QCOMPARE(m_dirLister.spyClear.count(), 0); 0316 0317 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0318 0319 // Give some time to KDirWatch 0320 QTest::qWait(1000); 0321 0322 KFileItem itemForUrl = KDirLister::cachedItemForUrl(itemUrl); 0323 QVERIFY(!itemForUrl.isNull()); 0324 QCOMPARE(itemForUrl.url().toString(), itemUrl.toString()); 0325 QCOMPARE(itemForUrl.entry().stringValue(KIO::UDSEntry::UDS_NAME), fileName); 0326 } 0327 0328 void KDirListerTest::testNewItemByCopyInSubDir() // #440712 0329 { 0330 // Test that copying a file to a directory whose parent is listed 0331 // triggers refreshItems for the directory 0332 const int origItemCount = m_items.count(); 0333 const QString path = tempPath(); 0334 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0335 connect(&m_dirLister, &KDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems); 0336 QSignalSpy refreshItemSpy(this, &KDirListerTest::refreshItemsReceived); 0337 0338 const QUrl subDirUrl = QUrl::fromLocalFile(path + "subdir"); 0339 KFileItem itemForUrl = KDirLister::cachedItemForUrl(subDirUrl); 0340 auto origModificationDate = itemForUrl.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); 0341 0342 const QString fileName = path + "subdir/toplevelfile_copy"; 0343 const QUrl itemUrl = QUrl::fromLocalFile(fileName); 0344 KIO::CopyJob *job = KIO::copyAs(QUrl::fromLocalFile(path + "toplevelfile_3"), itemUrl, KIO::HideProgressInfo); 0345 QVERIFY(job->exec()); 0346 0347 QTRY_COMPARE(m_items.count(), origItemCount); 0348 0349 // Give some time to KDirNotify 0350 QVERIFY(refreshItemSpy.wait(100)); 0351 0352 itemForUrl = KDirLister::cachedItemForUrl(subDirUrl); 0353 QVERIFY(itemForUrl.entry().numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME) > origModificationDate); 0354 0355 // clean leftover file 0356 QVERIFY(QFile(fileName).remove()); 0357 m_refreshedItems.clear(); 0358 } 0359 0360 void KDirListerTest::testNewItemsInSymlink() // #213799 0361 { 0362 const int origItemCount = m_items.count(); 0363 QCOMPARE(fileCount(), origItemCount); 0364 const QString path = tempPath(); 0365 QTemporaryFile tempFile; 0366 QVERIFY(tempFile.open()); 0367 const QString symPath = tempFile.fileName() + "_link"; 0368 tempFile.close(); 0369 const bool symlinkOk = KIOPrivate::createSymlink(path, symPath); 0370 if (!symlinkOk) { 0371 const QString error = 0372 QString::fromLatin1("Failed to create symlink '%1' pointing to '%2': %3").arg(symPath, path, QString::fromLocal8Bit(strerror(errno))); 0373 QVERIFY2(symlinkOk, qPrintable(error)); 0374 } 0375 MyDirLister dirLister2; 0376 m_items2.clear(); 0377 m_items.clear(); 0378 connect(&dirLister2, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems2); 0379 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0380 0381 // The initial listing 0382 m_dirLister.openUrl(QUrl::fromLocalFile(path)); 0383 dirLister2.openUrl(QUrl::fromLocalFile(symPath)); 0384 QTRY_COMPARE(m_items.count(), m_items2.count()); 0385 QTRY_COMPARE(m_items.count(), origItemCount); 0386 QTRY_VERIFY(dirLister2.isFinished()); 0387 0388 QTest::qWait(100); // We need a 1s timestamp difference on the dir, otherwise FAM won't notice anything. 0389 0390 qDebug() << "Creating new file"; 0391 const QString fileName = QStringLiteral("toplevelfile_newinlink"); 0392 createSimpleFile(path + fileName); 0393 0394 if (s_workaroundBrokenInotify) { 0395 org::kde::KDirNotify::emitFilesAdded(QUrl::fromLocalFile(path)); 0396 } 0397 0398 // Give time for KDirWatch to notify us 0399 QTRY_COMPARE(m_items2.count(), origItemCount + 1); 0400 QTRY_COMPARE(m_items.count(), origItemCount + 1); 0401 0402 // Now create an item using the symlink-path 0403 const QString fileName2 = QStringLiteral("toplevelfile_newinlink2"); 0404 { 0405 createSimpleFile(path + fileName2); 0406 0407 // Give time for KDirWatch to notify us 0408 QTRY_COMPARE(m_items2.count(), origItemCount + 2); 0409 QTRY_COMPARE(m_items.count(), origItemCount + 2); 0410 } 0411 QCOMPARE(fileCount(), m_items.count()); 0412 0413 // Test file deletion 0414 { 0415 qDebug() << "Deleting" << (path + fileName); 0416 QTest::qWait(100); // for timestamp difference 0417 QFile::remove(path + fileName); 0418 QTRY_COMPARE(dirLister2.spyItemsDeleted.count(), 1); 0419 QTRY_COMPARE(m_dirLister.spyItemsDeleted.count(), 1); 0420 const KFileItem item = dirLister2.spyItemsDeleted[0][0].value<KFileItemList>().at(0); 0421 QCOMPARE(item.url().toLocalFile(), QString(symPath + '/' + fileName)); 0422 } 0423 // Test file deletion in symlink dir #469254 0424 { 0425 qDebug() << "Deleting" << (symPath + "/" + fileName2); 0426 QTest::qWait(100); // for timestamp difference 0427 QFile::remove(symPath + "/" + fileName2); 0428 QTRY_COMPARE(dirLister2.spyItemsDeleted.count(), 2); 0429 QTRY_COMPARE(m_dirLister.spyItemsDeleted.count(), 2); 0430 const KFileItem item = dirLister2.spyItemsDeleted[1][0].value<KFileItemList>().at(0); 0431 QCOMPARE(item.url().toLocalFile(), QString(symPath + '/' + fileName2)); 0432 } 0433 0434 // TODO: test file update. 0435 disconnect(&m_dirLister, nullptr, this, nullptr); 0436 0437 QFile::remove(symPath); 0438 m_dirLister.openUrl(QUrl::fromLocalFile(tempPath())); 0439 } 0440 0441 // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. 0442 // Modifies one of the files to have html content 0443 void KDirListerTest::testRefreshItems() 0444 { 0445 m_refreshedItems.clear(); 0446 0447 const QString path = tempPath(); 0448 const QString fileName = path + "toplevelfile_1"; 0449 KFileItem cachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(fileName)); 0450 QVERIFY(!cachedItem.isNull()); 0451 QCOMPARE(cachedItem.mimetype(), QString("application/octet-stream")); 0452 0453 connect(&m_dirLister, &KCoreDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems); 0454 0455 QFile file(fileName); 0456 QVERIFY(file.open(QIODevice::Append)); 0457 file.write(QByteArray("<html>")); 0458 file.close(); 0459 QCOMPARE(QFileInfo(fileName).size(), 11LL /*Hello world*/ + 6 /*<html>*/); 0460 0461 QTRY_VERIFY(!m_refreshedItems.isEmpty()); 0462 0463 QCOMPARE(m_dirLister.spyStarted.count(), 0); // fast path: no directory listing needed 0464 QVERIFY(m_dirLister.spyCompleted.count() < 2); 0465 QVERIFY(m_dirLister.spyCompletedQUrl.count() < 2); 0466 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0467 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0468 QCOMPARE(m_dirLister.spyClear.count(), 0); 0469 0470 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0471 0472 QCOMPARE(m_refreshedItems.count(), 1); 0473 QPair<KFileItem, KFileItem> entry = m_refreshedItems.first(); 0474 QCOMPARE(entry.first.url().toLocalFile(), fileName); 0475 QCOMPARE(entry.first.size(), KIO::filesize_t(11)); 0476 QCOMPARE(entry.first.mimetype(), QString("application/octet-stream")); 0477 QCOMPARE(entry.second.url().toLocalFile(), fileName); 0478 QCOMPARE(entry.second.size(), KIO::filesize_t(11 /*Hello world*/ + 6 /*<html>*/)); 0479 QCOMPARE(entry.second.mimetype(), QString("text/html")); 0480 0481 // Let's see what KDirLister has in cache now 0482 cachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(fileName)); 0483 QCOMPARE(cachedItem.size(), KIO::filesize_t(11 /*Hello world*/ + 6 /*<html>*/)); 0484 m_refreshedItems.clear(); 0485 } 0486 0487 // Refresh the root item, plus a hidden file, e.g. changing its icon. #190535 0488 void KDirListerTest::testRefreshRootItem() 0489 { 0490 // This test assumes testOpenUrl was run before. So m_dirLister is holding the items already. 0491 m_refreshedItems.clear(); 0492 m_refreshedItems2.clear(); 0493 0494 // The item will be the root item of dirLister2, but also a child item 0495 // of m_dirLister. 0496 // In #190535 it would show "." instead of the subdir name, after a refresh... 0497 const QString path = tempPath() + "subdir"; 0498 MyDirLister dirLister2; 0499 fillDirLister2(dirLister2, path); 0500 0501 // Change the subdir by creating a file in it 0502 waitUntilMTimeChange(path); 0503 const QString foobar = path + "/.foobar"; 0504 createSimpleFile(foobar); 0505 0506 connect(&m_dirLister, &KCoreDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems); 0507 0508 // Arguably, the mtime change of "subdir" should lead to a refreshItem of subdir in the root dir. 0509 // So the next line shouldn't be necessary, if KDirLister did this correctly. This isn't what this test is about though. 0510 org::kde::KDirNotify::emitFilesChanged(QList<QUrl>{QUrl::fromLocalFile(path)}); 0511 QTRY_VERIFY(!m_refreshedItems.isEmpty()); 0512 0513 QCOMPARE(m_dirLister.spyStarted.count(), 0); 0514 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0515 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 0516 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0517 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0518 QCOMPARE(m_dirLister.spyClear.count(), 0); 0519 0520 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0521 0522 QCOMPARE(m_refreshedItems.count(), 1); 0523 QPair<KFileItem, KFileItem> entry = m_refreshedItems.first(); 0524 QCOMPARE(entry.first.url().toLocalFile(), path); 0525 QCOMPARE(entry.first.name(), QString("subdir")); 0526 QCOMPARE(entry.second.url().toLocalFile(), path); 0527 QCOMPARE(entry.second.name(), QString("subdir")); 0528 0529 QCOMPARE(m_refreshedItems2.count(), 1); 0530 entry = m_refreshedItems2.first(); 0531 QCOMPARE(entry.first.url().toLocalFile(), path); 0532 QCOMPARE(entry.second.url().toLocalFile(), path); 0533 // item name() doesn't matter here, it's the root item. 0534 0535 m_refreshedItems.clear(); 0536 m_refreshedItems2.clear(); 0537 0538 waitUntilMTimeChange(path); 0539 const QString directoryFile = path + "/.directory"; 0540 createSimpleFile(directoryFile); 0541 0542 org::kde::KDirNotify::emitFilesAdded(QUrl::fromLocalFile(path)); 0543 QTest::qWait(200); 0544 // The order of these two is not deterministic 0545 org::kde::KDirNotify::emitFilesChanged(QList<QUrl>{QUrl::fromLocalFile(directoryFile)}); 0546 org::kde::KDirNotify::emitFilesChanged(QList<QUrl>{QUrl::fromLocalFile(path)}); 0547 QTRY_VERIFY(!m_refreshedItems.isEmpty()); 0548 0549 QCOMPARE(m_refreshedItems.count(), 1); 0550 entry = m_refreshedItems.first(); 0551 QCOMPARE(entry.first.url().toLocalFile(), path); 0552 QCOMPARE(entry.second.url().toLocalFile(), path); 0553 0554 m_refreshedItems.clear(); 0555 m_refreshedItems2.clear(); 0556 0557 // Note: this test leaves the .directory file as a side effect. 0558 // Hidden though, shouldn't matter. 0559 } 0560 0561 void KDirListerTest::testDeleteItem() 0562 { 0563 testOpenUrl(); // ensure m_items is up-to-date 0564 0565 const int origItemCount = m_items.count(); 0566 QCOMPARE(fileCount(), origItemCount); 0567 const QString path = tempPath(); 0568 0569 // qDebug() << "Removing " << path+"toplevelfile_new"; 0570 QFile::remove(path + QString("toplevelfile_new")); 0571 // the remove() doesn't always trigger kdirwatch in stat mode, if this all happens in the same second 0572 KDirWatch::self()->setDirty(path); 0573 0574 // The signal should be emitted once with the deleted file 0575 QTRY_COMPARE(m_dirLister.spyItemsDeleted.count(), 1); 0576 0577 // OK now kdirlister told us the file was deleted, let's try a re-listing 0578 m_items.clear(); 0579 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0580 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0581 QVERIFY(!m_dirLister.isFinished()); 0582 0583 QTRY_COMPARE(m_items.count(), origItemCount - 1); 0584 QVERIFY(m_dirLister.isFinished()); 0585 0586 disconnect(&m_dirLister, nullptr, this, nullptr); 0587 QCOMPARE(fileCount(), m_items.count()); 0588 } 0589 0590 void KDirListerTest::testDeleteItems() 0591 { 0592 testOpenUrl(); // ensure m_items is up-to-date 0593 0594 const int origItemCount = m_items.count(); 0595 QCOMPARE(fileCount(), origItemCount); 0596 const QString path = tempPath(); 0597 0598 qDebug() << "Removing 100 files from " << path; 0599 for (int i = 0; i <= 100; ++i) { 0600 QFile::remove(path + QString("toplevelfile_new_%1").arg(i)); 0601 } 0602 // the remove() doesn't always trigger kdirwatch in stat mode, if this all happens in the same second 0603 KDirWatch::self()->setDirty(path); 0604 0605 // The signal could be emitted 1 time with all the deleted files or more times 0606 QTRY_VERIFY(m_dirLister.spyItemsDeleted.count() > 0); 0607 0608 // OK now kdirlister told us the file was deleted, let's try a re-listing 0609 m_items.clear(); 0610 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0611 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0612 QTRY_COMPARE(m_items.count(), origItemCount - 100); 0613 QVERIFY(m_dirLister.isFinished()); 0614 0615 disconnect(&m_dirLister, nullptr, this, nullptr); 0616 QCOMPARE(fileCount(), m_items.count()); 0617 } 0618 0619 void KDirListerTest::testRenameItem() 0620 { 0621 m_refreshedItems2.clear(); 0622 const QString dirPath = tempPath(); 0623 connect(&m_dirLister, &KCoreDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems2); 0624 const QString path = dirPath + "toplevelfile_2"; 0625 const QString newPath = dirPath + "toplevelfile_2.renamed.cpp"; 0626 0627 KIO::SimpleJob *job = KIO::rename(QUrl::fromLocalFile(path), QUrl::fromLocalFile(newPath), KIO::HideProgressInfo); 0628 QVERIFY(job->exec()); 0629 0630 QSignalSpy spyRefreshItems(&m_dirLister, &KCoreDirLister::refreshItems); 0631 QVERIFY(spyRefreshItems.wait(2000)); 0632 QTRY_COMPARE(m_refreshedItems2.count(), 1); 0633 QPair<KFileItem, KFileItem> entry = m_refreshedItems2.first(); 0634 QCOMPARE(entry.first.url().toLocalFile(), path); 0635 QCOMPARE(entry.first.mimetype(), QString("application/octet-stream")); 0636 QCOMPARE(entry.second.url().toLocalFile(), newPath); 0637 QCOMPARE(entry.second.mimetype(), QString("text/x-c++src")); 0638 disconnect(&m_dirLister, nullptr, this, nullptr); 0639 0640 // Let's see what KDirLister has in cache now 0641 KFileItem cachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(newPath)); 0642 QVERIFY(!cachedItem.isNull()); 0643 QCOMPARE(cachedItem.url().toLocalFile(), newPath); 0644 KFileItem oldCachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(path)); 0645 QVERIFY(oldCachedItem.isNull()); 0646 m_refreshedItems2.clear(); 0647 } 0648 0649 void KDirListerTest::testRenameAndOverwrite() // has to be run after testRenameItem 0650 { 0651 // Rename toplevelfile_2.renamed.html to toplevelfile_2, overwriting it. 0652 const QString dirPath = tempPath(); 0653 const QString path = dirPath + "toplevelfile_2"; 0654 createTestFile(path); 0655 0656 if (s_workaroundBrokenInotify) { 0657 org::kde::KDirNotify::emitFilesAdded(QUrl::fromLocalFile(dirPath)); 0658 } 0659 0660 KFileItem existingItem; 0661 while (existingItem.isNull()) { 0662 QTest::qWait(100); 0663 existingItem = m_dirLister.findByUrl(QUrl::fromLocalFile(path)); 0664 }; 0665 QCOMPARE(existingItem.url().toLocalFile(), path); 0666 0667 m_refreshedItems.clear(); 0668 connect(&m_dirLister, &KCoreDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems); 0669 const QString newPath = dirPath + "toplevelfile_2.renamed.cpp"; 0670 0671 KIO::SimpleJob *job = KIO::rename(QUrl::fromLocalFile(newPath), QUrl::fromLocalFile(path), KIO::Overwrite | KIO::HideProgressInfo); 0672 bool ok = job->exec(); 0673 QVERIFY(ok); 0674 0675 if (m_refreshedItems.isEmpty()) { 0676 QTRY_VERIFY(!m_refreshedItems.isEmpty()); // could come from KDirWatch or KDirNotify. 0677 } 0678 0679 // Check that itemsDeleted was emitted -- preferably BEFORE refreshItems, 0680 // but we can't easily check that with QSignalSpy... 0681 QTRY_COMPARE(m_dirLister.spyItemsDeleted.count(), 1); 0682 0683 QCOMPARE(m_refreshedItems.count(), 1); 0684 QPair<KFileItem, KFileItem> entry = m_refreshedItems.first(); 0685 QCOMPARE(entry.first.url().toLocalFile(), newPath); 0686 QCOMPARE(entry.second.url().toLocalFile(), path); 0687 disconnect(&m_dirLister, nullptr, this, nullptr); 0688 0689 // Let's see what KDirLister has in cache now 0690 KFileItem cachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(path)); 0691 QCOMPARE(cachedItem.url().toLocalFile(), path); 0692 KFileItem oldCachedItem = m_dirLister.findByUrl(QUrl::fromLocalFile(newPath)); 0693 QVERIFY(oldCachedItem.isNull()); 0694 m_refreshedItems.clear(); 0695 } 0696 0697 void KDirListerTest::testConcurrentListing() 0698 { 0699 const int origItemCount = m_items.count(); 0700 QCOMPARE(fileCount(), origItemCount); 0701 m_items.clear(); 0702 m_items2.clear(); 0703 0704 MyDirLister dirLister2; 0705 0706 const QString path = tempPath(); 0707 0708 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0709 connect(&dirLister2, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems2); 0710 0711 // Before dirLister2 has time to emit the items, let's make m_dirLister move to another dir. 0712 // This reproduces the use case "clicking on a folder in dolphin iconview, and dirlister2 0713 // is the one used by the "folder panel". m_dirLister is going to list the subdir, 0714 // while dirLister2 wants to list the folder that m_dirLister has just left. 0715 dirLister2.stop(); // like dolphin does, noop. 0716 dirLister2.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0717 m_dirLister.openUrl(QUrl::fromLocalFile(path + "subdir"), KDirLister::NoFlags); 0718 0719 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0720 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0721 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 0722 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0723 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0724 QCOMPARE(m_dirLister.spyClear.count(), 1); 0725 0726 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0727 0728 QCOMPARE(m_items.count(), 0); 0729 0730 QCOMPARE(dirLister2.spyStarted.count(), 1); 0731 QCOMPARE(dirLister2.spyCompleted.count(), 0); 0732 QCOMPARE(dirLister2.spyCompletedQUrl.count(), 0); 0733 QCOMPARE(dirLister2.spyCanceled.count(), 0); 0734 QCOMPARE(dirLister2.spyCanceledQUrl.count(), 0); 0735 QCOMPARE(dirLister2.spyClear.count(), 1); 0736 0737 QCOMPARE(dirLister2.spyClearDir.count(), 0); 0738 0739 QCOMPARE(m_items2.count(), 0); 0740 QVERIFY(!m_dirLister.isFinished()); 0741 QVERIFY(!dirLister2.isFinished()); 0742 0743 // then wait for completed 0744 qDebug("waiting for completed"); 0745 0746 // QCOMPARE(m_dirLister.spyStarted.count(), 1); // 2 when subdir is already in cache. 0747 QTRY_COMPARE(m_dirLister.spyCompleted.count(), 1); 0748 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0749 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0750 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0751 QCOMPARE(m_dirLister.spyClear.count(), 1); 0752 0753 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0754 0755 QCOMPARE(m_items.count(), 3); 0756 0757 QTRY_COMPARE(dirLister2.spyStarted.count(), 1); 0758 QTRY_COMPARE(dirLister2.spyCompleted.count(), 1); 0759 QCOMPARE(dirLister2.spyCompletedQUrl.count(), 1); 0760 QCOMPARE(dirLister2.spyCanceled.count(), 0); 0761 QCOMPARE(dirLister2.spyCanceledQUrl.count(), 0); 0762 QCOMPARE(dirLister2.spyClear.count(), 1); 0763 0764 QCOMPARE(dirLister2.spyClearDir.count(), 0); 0765 0766 QCOMPARE(m_items2.count(), origItemCount); 0767 if (!m_dirLister.isFinished()) { // false when an update is running because subdir is already in cache 0768 // TODO check why this fails QVERIFY(m_dirLister.spyCanceled.wait(1000)); 0769 QTest::qWait(1000); 0770 } 0771 0772 disconnect(&m_dirLister, nullptr, this, nullptr); 0773 disconnect(&dirLister2, nullptr, this, nullptr); 0774 } 0775 0776 void KDirListerTest::testConcurrentHoldingListing() 0777 { 0778 // #167851. 0779 // A dirlister holding the items, and a second dirlister does 0780 // openUrl(reload) (which triggers updateDirectory()) 0781 // and the first lister immediately does openUrl() (which emits cached items). 0782 0783 testOpenUrl(); // ensure m_dirLister holds the items. 0784 const int origItemCount = m_items.count(); 0785 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0786 0787 m_items.clear(); 0788 m_items2.clear(); 0789 const QString path = tempPath(); 0790 MyDirLister dirLister2; 0791 connect(&dirLister2, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems2); 0792 0793 dirLister2.openUrl(QUrl::fromLocalFile(path), KDirLister::Reload); // will start a list job 0794 QCOMPARE(dirLister2.spyStarted.count(), 1); 0795 QCOMPARE(dirLister2.spyCompleted.count(), 0); 0796 QCOMPARE(m_items.count(), 0); 0797 QCOMPARE(m_items2.count(), 0); 0798 0799 qDebug("calling m_dirLister.openUrl"); 0800 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); // should emit cached items, and then "join" the running listjob 0801 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0802 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0803 QCOMPARE(m_items.count(), 0); 0804 QCOMPARE(m_items2.count(), 0); 0805 0806 qDebug("waiting for completed"); 0807 QTRY_COMPARE(dirLister2.spyStarted.count(), 1); 0808 QTRY_COMPARE(dirLister2.spyCompleted.count(), 1); 0809 QCOMPARE(dirLister2.spyCompletedQUrl.count(), 1); 0810 QCOMPARE(dirLister2.spyCanceled.count(), 0); 0811 QCOMPARE(dirLister2.spyCanceledQUrl.count(), 0); 0812 QCOMPARE(dirLister2.spyClear.count(), 1); 0813 0814 QCOMPARE(dirLister2.spyClearDir.count(), 0); 0815 0816 QCOMPARE(m_items2.count(), origItemCount); 0817 0818 QTRY_COMPARE(m_dirLister.spyStarted.count(), 1); 0819 QTRY_COMPARE(m_dirLister.spyCompleted.count(), 1); 0820 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0821 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0822 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0823 QCOMPARE(m_dirLister.spyClear.count(), 1); 0824 0825 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0826 0827 QVERIFY(dirLister2.isFinished()); 0828 QVERIFY(m_dirLister.isFinished()); 0829 disconnect(&m_dirLister, nullptr, this, nullptr); 0830 QCOMPARE(m_items.count(), origItemCount); 0831 } 0832 0833 void KDirListerTest::testConcurrentListingAndStop() 0834 { 0835 m_items.clear(); 0836 m_items2.clear(); 0837 0838 MyDirLister dirLister2; 0839 0840 // Use a new tempdir for this test, so that we don't use the cache at all. 0841 QTemporaryDir tempDir(homeTmpDir()); 0842 const QString path = tempDir.path() + '/'; 0843 createTestFile(path + "file_1"); 0844 createTestFile(path + "file_2"); 0845 createTestFile(path + "file_3"); 0846 0847 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0848 connect(&dirLister2, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems2); 0849 0850 // Before m_dirLister has time to emit the items, let's make dirLister2 call stop(). 0851 // This should not stop the list job for m_dirLister (#267709). 0852 dirLister2.openUrl(QUrl::fromLocalFile(path), KDirLister::Reload); 0853 m_dirLister.openUrl(QUrl::fromLocalFile(path) /*, KDirLister::Reload*/); 0854 0855 QCOMPARE(m_dirLister.spyStarted.count(), 1); 0856 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 0857 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 0858 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0859 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0860 QCOMPARE(m_dirLister.spyClear.count(), 1); 0861 0862 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0863 0864 QCOMPARE(m_items.count(), 0); 0865 0866 QCOMPARE(dirLister2.spyStarted.count(), 1); 0867 QCOMPARE(dirLister2.spyCompleted.count(), 0); 0868 QCOMPARE(dirLister2.spyCompletedQUrl.count(), 0); 0869 QCOMPARE(dirLister2.spyCanceled.count(), 0); 0870 QCOMPARE(dirLister2.spyCanceledQUrl.count(), 0); 0871 QCOMPARE(dirLister2.spyClear.count(), 1); 0872 0873 QCOMPARE(dirLister2.spyClearDir.count(), 0); 0874 0875 QCOMPARE(m_items2.count(), 0); 0876 QVERIFY(!m_dirLister.isFinished()); 0877 QVERIFY(!dirLister2.isFinished()); 0878 0879 dirLister2.stop(); 0880 0881 QCOMPARE(dirLister2.spyStarted.count(), 1); 0882 QCOMPARE(dirLister2.spyCompleted.count(), 0); 0883 QCOMPARE(dirLister2.spyCompletedQUrl.count(), 0); 0884 QCOMPARE(dirLister2.spyCanceled.count(), 1); 0885 QCOMPARE(dirLister2.spyCanceledQUrl.count(), 1); 0886 QCOMPARE(dirLister2.spyClear.count(), 1); 0887 0888 QCOMPARE(dirLister2.spyClearDir.count(), 0); 0889 0890 QCOMPARE(m_items2.count(), 0); 0891 0892 // then wait for completed 0893 qDebug("waiting for completed"); 0894 QTRY_COMPARE(m_items.count(), 3); 0895 QTRY_COMPARE(m_items2.count(), 0); 0896 QTRY_VERIFY(m_dirLister.isFinished()); 0897 0898 // QCOMPARE(m_dirLister.spyStarted.count(), 1); // 2 when in cache 0899 QCOMPARE(m_dirLister.spyCompleted.count(), 1); 0900 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 1); 0901 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 0902 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 0903 QCOMPARE(m_dirLister.spyClear.count(), 1); 0904 0905 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 0906 0907 disconnect(&m_dirLister, nullptr, this, nullptr); 0908 } 0909 0910 void KDirListerTest::testDeleteListerEarly() 0911 { 0912 // Do the same again, it should behave the same, even with the items in the cache 0913 testOpenUrl(); 0914 0915 // Start a second lister, it will get a cached items job, but delete it before the job can run 0916 // qDebug() << "=========================================="; 0917 { 0918 m_items.clear(); 0919 const QString path = tempPath(); 0920 MyDirLister secondDirLister; 0921 secondDirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 0922 QVERIFY(!secondDirLister.isFinished()); 0923 } 0924 // qDebug() << "=========================================="; 0925 0926 // Check if we didn't keep the deleted dirlister in one of our lists. 0927 // I guess the best way to do that is to just list the same dir again. 0928 testOpenUrl(); 0929 } 0930 0931 void KDirListerTest::testOpenUrlTwice() 0932 { 0933 // Calling openUrl(reload)+openUrl(normal) before listing even starts. 0934 const int origItemCount = m_items.count(); 0935 m_items.clear(); 0936 const QString path = tempPath(); 0937 MyDirLister secondDirLister; 0938 connect(&secondDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0939 0940 secondDirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::Reload); // will start 0941 QCOMPARE(secondDirLister.spyStarted.count(), 1); 0942 QCOMPARE(secondDirLister.spyCompleted.count(), 0); 0943 0944 qDebug("calling openUrl again"); 0945 secondDirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); // will stop + start 0946 0947 qDebug("waiting for completed"); 0948 QTRY_COMPARE(secondDirLister.spyStarted.count(), 2); 0949 QTRY_COMPARE(secondDirLister.spyCompleted.count(), 1); 0950 QCOMPARE(secondDirLister.spyCompletedQUrl.count(), 1); 0951 QCOMPARE(secondDirLister.spyCanceled.count(), 0); // should not be emitted, see next test 0952 QCOMPARE(secondDirLister.spyCanceledQUrl.count(), 0); 0953 QCOMPARE(secondDirLister.spyClear.count(), 2); 0954 0955 QCOMPARE(secondDirLister.spyClearDir.count(), 0); 0956 0957 if (origItemCount) { // 0 if running this test separately 0958 QCOMPARE(m_items.count(), origItemCount); 0959 } 0960 QVERIFY(secondDirLister.isFinished()); 0961 disconnect(&secondDirLister, nullptr, this, nullptr); 0962 } 0963 0964 void KDirListerTest::testOpenUrlTwiceWithKeep() 0965 { 0966 // Calling openUrl(reload)+openUrl(keep) on a new dir, 0967 // before listing even starts (#177387) 0968 // Well, in 177387 the second openUrl call was made from within slotCanceled 0969 // called by the first openUrl 0970 // (slotLoadingFinished -> setCurrentItem -> expandToUrl -> listDir), 0971 // which messed things up in kdirlister (unexpected reentrancy). 0972 m_items.clear(); 0973 const QString path = tempPath() + "newsubdir"; 0974 QDir().mkdir(path); 0975 MyDirLister secondDirLister; 0976 connect(&secondDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 0977 0978 secondDirLister.openUrl(QUrl::fromLocalFile(path)); // will start a list job 0979 QCOMPARE(secondDirLister.spyStarted.count(), 1); 0980 QCOMPARE(secondDirLister.spyCanceled.count(), 0); 0981 QCOMPARE(secondDirLister.spyCompleted.count(), 0); 0982 0983 qDebug("calling openUrl again"); 0984 secondDirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::Keep); // stops and restarts the job 0985 0986 qDebug("waiting for completed"); 0987 QTRY_COMPARE(secondDirLister.spyStarted.count(), 2); 0988 QTRY_COMPARE(secondDirLister.spyCompleted.count(), 1); 0989 QCOMPARE(secondDirLister.spyCompletedQUrl.count(), 1); 0990 QCOMPARE(secondDirLister.spyCanceled.count(), 0); // should not be emitted, it led to recursion 0991 QCOMPARE(secondDirLister.spyCanceledQUrl.count(), 0); 0992 QCOMPARE(secondDirLister.spyClear.count(), 1); 0993 0994 QCOMPARE(secondDirLister.spyClearDir.count(), 1); 0995 0996 QCOMPARE(m_items.count(), 0); 0997 QVERIFY(secondDirLister.isFinished()); 0998 disconnect(&secondDirLister, nullptr, this, nullptr); 0999 1000 QDir().remove(path); 1001 } 1002 1003 void KDirListerTest::testOpenAndStop() 1004 { 1005 m_items.clear(); 1006 const QString path = QStringLiteral("/"); // better not use a directory that we already listed! 1007 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1008 1009 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 1010 qDebug() << "Calling stop!"; 1011 m_dirLister.stop(); // we should also test stop(QUrl::fromLocalFile(path))... 1012 1013 QCOMPARE(m_dirLister.spyStarted.count(), 1); // The call to openUrl itself, emits started 1014 QCOMPARE(m_dirLister.spyCompleted.count(), 0); // we had time to stop before the job even started 1015 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 1016 QCOMPARE(m_dirLister.spyCanceled.count(), 1); 1017 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 1); 1018 QCOMPARE(m_dirLister.spyClear.count(), 1); 1019 1020 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 1021 1022 QCOMPARE(m_items.count(), 0); // we had time to stop before the job even started 1023 QVERIFY(m_dirLister.isFinished()); 1024 disconnect(&m_dirLister, nullptr, this, nullptr); 1025 } 1026 1027 // A bug in the decAutoUpdate/incAutoUpdate logic made KDirLister stop watching a directory for changes, 1028 // and never watch it again when opening it from the cache. 1029 void KDirListerTest::testBug211472() 1030 { 1031 m_items.clear(); 1032 1033 QTemporaryDir newDir(homeTmpDir()); 1034 const QString path = newDir.path() + "/newsubdir/"; 1035 QDir().mkdir(path); 1036 MyDirLister dirLister; 1037 connect(&dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1038 1039 dirLister.openUrl(QUrl::fromLocalFile(path)); 1040 QVERIFY(dirLister.spyCompleted.wait(1000)); 1041 QVERIFY(dirLister.isFinished()); 1042 QVERIFY(m_items.isEmpty()); 1043 1044 // This block is required to trigger bug 211472. 1045 1046 // Go 'up' to the parent of 'newsubdir'. 1047 dirLister.openUrl(QUrl::fromLocalFile(newDir.path())); 1048 QVERIFY(dirLister.spyCompleted.wait(1000)); 1049 QTRY_VERIFY(dirLister.isFinished()); 1050 QTRY_VERIFY(!m_items.isEmpty()); 1051 m_items.clear(); 1052 1053 // Create a file in 'newsubdir' while we are listing its parent dir. 1054 createTestFile(path + "newFile-1"); 1055 // At this point, newsubdir is not used, so it's moved to the cache. 1056 // This happens in checkUpdate, called when receiving a notification for the cached dir, 1057 // this is why this unittest needs to create a test file in the subdir. 1058 1059 // wait a second and ensure the list is still empty afterwards 1060 QTest::qWait(1000); 1061 QTRY_VERIFY(m_items.isEmpty()); 1062 1063 // Return to 'newsubdir'. It will be emitted from the cache, then an update will happen. 1064 dirLister.openUrl(QUrl::fromLocalFile(path)); 1065 // Check that completed is emitted twice 1066 QVERIFY(dirLister.spyCompleted.wait(1000)); 1067 QVERIFY(dirLister.spyCompleted.wait(1000)); 1068 QTRY_VERIFY(dirLister.isFinished()); 1069 QTRY_COMPARE(m_items.count(), 1); 1070 m_items.clear(); 1071 1072 // Now try to create a second file in 'newsubdir' and verify that the 1073 // dir lister notices it. 1074 QTest::qWait(1000); // We need a 1s timestamp difference on the dir, otherwise FAM won't notice anything. 1075 1076 createTestFile(path + "newFile-2"); 1077 QTRY_COMPARE(m_items.count(), 1); 1078 1079 newDir.remove(); 1080 QSignalSpy spyClear(&dirLister, qOverload<>(&KCoreDirLister::clear)); 1081 QVERIFY(spyClear.wait(1000)); 1082 } 1083 1084 void KDirListerTest::testRenameCurrentDir() // #294445 1085 { 1086 m_items.clear(); 1087 1088 const QString path = tempPath() + "newsubdir-1"; 1089 QVERIFY(QDir().mkdir(path)); 1090 MyDirLister secondDirLister; 1091 connect(&secondDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1092 1093 secondDirLister.openUrl(QUrl::fromLocalFile(path)); 1094 QSignalSpy spyCompleted(&secondDirLister, qOverload<>(&KCoreDirLister::completed)); 1095 QVERIFY(spyCompleted.wait(1000)); 1096 QVERIFY(secondDirLister.isFinished()); 1097 QVERIFY(m_items.empty()); 1098 QCOMPARE(secondDirLister.rootItem().url().toLocalFile(), path); 1099 1100 const QString newPath = tempPath() + "newsubdir-2"; 1101 QVERIFY(QDir().rename(path, newPath)); 1102 org::kde::KDirNotify::emitFileRenamed(QUrl::fromLocalFile(path), QUrl::fromLocalFile(newPath)); 1103 QSignalSpy spyRedirection(&secondDirLister, &KCoreDirLister::redirection); 1104 QVERIFY(spyRedirection.wait(1000)); 1105 1106 // Check that the URL of the root item got updated 1107 QCOMPARE(secondDirLister.rootItem().url().toLocalFile(), newPath); 1108 1109 disconnect(&secondDirLister, nullptr, this, nullptr); 1110 QDir().rmdir(newPath); 1111 } 1112 1113 void KDirListerTest::slotOpenUrlOnRename(const QUrl &newUrl) 1114 { 1115 QVERIFY(m_dirLister.openUrl(newUrl)); 1116 } 1117 1118 // This tests for a crash if you connect redirects to openUrl, due 1119 // to internal data being inconsistently exposed. 1120 // Matches usage in gwenview. 1121 void KDirListerTest::testRenameCurrentDirOpenUrl() 1122 { 1123 m_items.clear(); 1124 const QString path = tempPath() + "newsubdir-1/"; 1125 QVERIFY(QDir().mkdir(path)); 1126 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1127 1128 m_dirLister.openUrl(QUrl::fromLocalFile(path)); 1129 QSignalSpy spyCompleted(&m_dirLister, qOverload<>(&KCoreDirLister::completed)); 1130 // Wait for the signal completed to be emitted 1131 QVERIFY(spyCompleted.wait(1000)); 1132 QVERIFY(m_dirLister.isFinished()); 1133 1134 const QString newPath = tempPath() + "newsubdir-2"; 1135 QVERIFY(QDir().rename(path, newPath)); 1136 1137 org::kde::KDirNotify::emitFileRenamed(QUrl::fromLocalFile(path), QUrl::fromLocalFile(newPath)); 1138 1139 // Connect the redirection to openURL, so that on a rename the new location is opened. 1140 // This matches usage in gwenview, and crashes 1141 connect(&m_dirLister, &KCoreDirLister::redirection, this, [this](const QUrl &, const QUrl &newUrl) { 1142 slotOpenUrlOnRename(newUrl); 1143 }); 1144 1145 QTRY_VERIFY(m_dirLister.isFinished()); 1146 disconnect(&m_dirLister, nullptr, this, nullptr); 1147 QDir().rmdir(newPath); 1148 } 1149 1150 void KDirListerTest::testRedirection() 1151 { 1152 m_items.clear(); 1153 const QUrl url(QStringLiteral("file://somemachine/")); 1154 1155 if (!KProtocolInfo::isKnownProtocol(QStringLiteral("smb"))) { 1156 QSKIP("smb not installed"); 1157 } 1158 1159 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1160 // The call to openUrl itself, emits started 1161 m_dirLister.openUrl(url, KDirLister::NoFlags); 1162 1163 QCOMPARE(m_dirLister.spyStarted.count(), 1); 1164 QCOMPARE(m_dirLister.spyCompleted.count(), 0); 1165 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 1166 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 1167 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 1168 QCOMPARE(m_dirLister.spyClear.count(), 1); 1169 1170 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 1171 1172 QCOMPARE(m_dirLister.spyRedirection.count(), 0); 1173 QCOMPARE(m_items.count(), 0); 1174 QVERIFY(!m_dirLister.isFinished()); 1175 1176 // then wait for the redirection signal 1177 qDebug("waiting for redirection"); 1178 QTRY_COMPARE(m_dirLister.spyStarted.count(), 1); 1179 QCOMPARE(m_dirLister.spyCompleted.count(), 0); // we stopped before the listing. 1180 QCOMPARE(m_dirLister.spyCompletedQUrl.count(), 0); 1181 QCOMPARE(m_dirLister.spyCanceled.count(), 0); 1182 QCOMPARE(m_dirLister.spyCanceledQUrl.count(), 0); 1183 QTRY_COMPARE(m_dirLister.spyClear.count(), 2); // redirection cleared a second time (just in case...) 1184 1185 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 1186 1187 QTRY_COMPARE(m_dirLister.spyRedirection.count(), 1); 1188 QVERIFY(m_items.isEmpty()); 1189 QVERIFY(!m_dirLister.isFinished()); 1190 1191 m_dirLister.stop(url); 1192 QVERIFY(!m_dirLister.isFinished()); 1193 disconnect(&m_dirLister, nullptr, this, nullptr); 1194 } 1195 1196 void KDirListerTest::testListEmptyDirFromCache() // #278431 1197 { 1198 m_items.clear(); 1199 1200 QTemporaryDir newDir(homeTmpDir()); 1201 const QUrl url = QUrl::fromLocalFile(newDir.path()); 1202 1203 // List and watch an empty dir 1204 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1205 m_dirLister.openUrl(url); 1206 QSignalSpy spyCompleted(&m_dirLister, qOverload<>(&KCoreDirLister::completed)); 1207 QVERIFY(spyCompleted.wait(1000)); 1208 QVERIFY(m_dirLister.isFinished()); 1209 QVERIFY(m_items.isEmpty()); 1210 1211 // List it with two more dirlisters (one will create a cached items job, the second should also benefit from it) 1212 MyDirLister secondDirLister; 1213 connect(&secondDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1214 secondDirLister.openUrl(url); 1215 MyDirLister thirdDirLister; 1216 connect(&thirdDirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1217 thirdDirLister.openUrl(url); 1218 1219 // The point of this test is that (with DEBUG_CACHE enabled) it used to assert here 1220 // with "HUH? Lister KDirLister(0x7ffd1f044260) is supposed to be listing, but has no job!" 1221 // due to the if (!itemU->lstItems.isEmpty()) check which is now removed. 1222 1223 QVERIFY(!secondDirLister.isFinished()); // we didn't go to the event loop yet 1224 QSignalSpy spySecondCompleted(&secondDirLister, qOverload<>(&KCoreDirLister::completed)); 1225 QVERIFY(spySecondCompleted.wait(1000)); 1226 if (!thirdDirLister.isFinished()) { 1227 QSignalSpy spyThirdCompleted(&thirdDirLister, qOverload<>(&KCoreDirLister::completed)); 1228 QVERIFY(spyThirdCompleted.wait(1000)); 1229 } 1230 } 1231 1232 void KDirListerTest::testWatchingAfterCopyJob() // #331582 1233 { 1234 m_items.clear(); 1235 1236 QTemporaryDir newDir(homeTmpDir()); 1237 const QString path = newDir.path() + '/'; 1238 1239 // List and watch an empty dir 1240 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1241 m_dirLister.openUrl(QUrl::fromLocalFile(path)); 1242 QSignalSpy spyCompleted(&m_dirLister, qOverload<>(&KCoreDirLister::completed)); 1243 QVERIFY(spyCompleted.wait(1000)); 1244 QVERIFY(m_dirLister.isFinished()); 1245 QVERIFY(m_items.isEmpty()); 1246 1247 // Create three subfolders. 1248 QVERIFY(QDir().mkdir(path + "New Folder")); 1249 QVERIFY(QDir().mkdir(path + "New Folder 1")); 1250 QVERIFY(QDir().mkdir(path + "New Folder 2")); 1251 1252 QVERIFY(spyCompleted.wait(1000)); 1253 QTRY_VERIFY(m_dirLister.isFinished()); 1254 QTRY_COMPARE(m_items.count(), 3); 1255 1256 // Create a new file and verify that the dir lister notices it. 1257 m_items.clear(); 1258 createTestFile(path + QLatin1Char('a')); 1259 QVERIFY(spyCompleted.wait(1000)); 1260 QTRY_VERIFY(m_dirLister.isFinished()); 1261 QTRY_COMPARE(m_items.count(), 1); 1262 1263 // Rename one of the subfolders. 1264 const QString oldPath = path + "New Folder 1"; 1265 const QString newPath = path + "New Folder 1a"; 1266 1267 // NOTE: The following two lines are required to trigger the bug! 1268 KIO::Job *job = KIO::moveAs(QUrl::fromLocalFile(oldPath), QUrl::fromLocalFile(newPath), KIO::HideProgressInfo); 1269 job->exec(); 1270 1271 // Now try to create a second new file and verify that the 1272 // dir lister notices it. 1273 m_items.clear(); 1274 createTestFile(path + QLatin1Char('b')); 1275 1276 // This should end up in "KCoreDirListerCache::slotFileDirty" 1277 QTRY_COMPARE(m_items.count(), 1); 1278 1279 newDir.remove(); 1280 QSignalSpy clearSpy(&m_dirLister, qOverload<>(&KCoreDirLister::clear)); 1281 QVERIFY(clearSpy.wait(1000)); 1282 } 1283 1284 void KDirListerTest::testRemoveWatchedDirectory() 1285 { 1286 m_items.clear(); 1287 1288 QTemporaryDir newDir(homeTmpDir()); 1289 const QString path = newDir.path() + '/'; 1290 1291 // List and watch an empty dir 1292 connect(&m_dirLister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1293 m_dirLister.openUrl(QUrl::fromLocalFile(path)); 1294 QSignalSpy spyCompleted(&m_dirLister, qOverload<>(&KCoreDirLister::completed)); 1295 QVERIFY(spyCompleted.wait(1000)); 1296 QTRY_VERIFY(m_dirLister.isFinished()); 1297 QTRY_VERIFY(m_items.isEmpty()); 1298 1299 // Create a subfolder. 1300 const QString subDirPath = path + "abc"; 1301 QVERIFY(QDir().mkdir(subDirPath)); 1302 1303 QVERIFY(spyCompleted.wait(1000)); 1304 QTRY_VERIFY(m_dirLister.isFinished()); 1305 QTRY_COMPARE(m_items.count(), 1); 1306 const KFileItem item = m_items.at(0); 1307 1308 // Watch the subfolder for changes, independently. 1309 // This is what triggers the bug. 1310 // (Technically, this could become a KDirWatch unittest, but if one day we use QFSW, good to have the tests here) 1311 KDirWatch watcher; 1312 watcher.addDir(subDirPath); 1313 1314 // Remove the subfolder. 1315 m_items.clear(); 1316 QVERIFY(QDir().rmdir(path + "abc")); 1317 1318 // This should trigger an update. 1319 QVERIFY(spyCompleted.wait(1000)); 1320 QVERIFY(m_dirLister.isFinished()); 1321 QCOMPARE(m_items.count(), 0); 1322 QCOMPARE(m_dirLister.spyItemsDeleted.count(), 1); 1323 const KFileItem deletedItem = m_dirLister.spyItemsDeleted.at(0).at(0).value<KFileItemList>().at(0); 1324 QCOMPARE(item, deletedItem); 1325 } 1326 1327 void KDirListerTest::testDirPermissionChange() 1328 { 1329 QTemporaryDir tempDir(homeTmpDir()); 1330 1331 const QString path = tempDir.path() + '/'; 1332 const QString subdir = path + QLatin1String("subdir"); 1333 QVERIFY(QDir().mkdir(subdir)); 1334 1335 MyDirLister mylister; 1336 mylister.openUrl(QUrl::fromLocalFile(tempDir.path())); 1337 QSignalSpy spyCompleted(&mylister, qOverload<>(&KCoreDirLister::completed)); 1338 QVERIFY(spyCompleted.wait(1000)); 1339 1340 KFileItemList list = mylister.items(); 1341 QVERIFY(mylister.isFinished()); 1342 QCOMPARE(list.count(), 1); 1343 QCOMPARE(mylister.rootItem().url().toLocalFile(), tempDir.path()); 1344 1345 const mode_t permissions = (S_IRUSR | S_IWUSR | S_IXUSR); 1346 KIO::SimpleJob *job = KIO::chmod(list.first().url(), permissions); 1347 QVERIFY(job->exec()); 1348 1349 QSignalSpy spyRefreshItems(&mylister, &KCoreDirLister::refreshItems); 1350 QVERIFY(spyRefreshItems.wait(2000)); 1351 1352 list = mylister.items(); 1353 QCOMPARE(list.first().permissions(), permissions); 1354 QVERIFY(QDir().rmdir(subdir)); 1355 } 1356 1357 void KDirListerTest::slotNewItems(const KFileItemList &lst) 1358 { 1359 m_items += lst; 1360 } 1361 1362 void KDirListerTest::slotNewItems2(const KFileItemList &lst) 1363 { 1364 m_items2 += lst; 1365 } 1366 1367 void KDirListerTest::slotRefreshItems(const QList<QPair<KFileItem, KFileItem>> &lst) 1368 { 1369 m_refreshedItems += lst; 1370 Q_EMIT refreshItemsReceived(); 1371 } 1372 1373 void KDirListerTest::slotRefreshItems2(const QList<QPair<KFileItem, KFileItem>> &lst) 1374 { 1375 m_refreshedItems2 += lst; 1376 } 1377 1378 void KDirListerTest::testCopyAfterListingAndMove() // #353195 1379 { 1380 const QString dirA = tempPath() + "a"; 1381 QVERIFY(QDir().mkdir(dirA)); 1382 const QString dirB = tempPath() + "b"; 1383 QVERIFY(QDir().mkdir(dirB)); 1384 1385 // ensure m_dirLister holds the items. 1386 m_dirLister.openUrl(QUrl::fromLocalFile(tempPath()), KDirLister::NoFlags); 1387 QSignalSpy spyCompleted(&m_dirLister, qOverload<>(&KCoreDirLister::completed)); 1388 QVERIFY(spyCompleted.wait()); 1389 1390 // Move b into a 1391 KIO::Job *moveJob = KIO::move(QUrl::fromLocalFile(dirB), QUrl::fromLocalFile(dirA)); 1392 moveJob->setUiDelegate(nullptr); 1393 QVERIFY(moveJob->exec()); 1394 QVERIFY(QFileInfo(tempPath() + "a/b").isDir()); 1395 1396 // Give some time to processPendingUpdates 1397 QTest::qWait(1000); 1398 1399 // Copy folder a elsewhere 1400 const QString dest = tempPath() + "subdir"; 1401 KIO::Job *copyJob = KIO::copy(QUrl::fromLocalFile(dirA), QUrl::fromLocalFile(dest)); 1402 copyJob->setUiDelegate(nullptr); 1403 QVERIFY(copyJob->exec()); 1404 QVERIFY(QFileInfo(tempPath() + "subdir/a/b").isDir()); 1405 } 1406 1407 void KDirListerTest::testRenameDirectory() // #401552 1408 { 1409 // Create the directory structure to reproduce the bug in a reliable way 1410 const QString dirW = tempPath() + "w"; 1411 QVERIFY(QDir().mkdir(dirW)); 1412 const QString dirW1 = tempPath() + "w/Files"; 1413 QVERIFY(QDir().mkdir(dirW1)); 1414 const QString dirW2 = tempPath() + "w/Files/Files"; 1415 QVERIFY(QDir().mkdir(dirW2)); 1416 // Place some empty files in each directory 1417 for (int i = 0; i < 50; i++) { 1418 createSimpleFile(dirW + QString("t_%1").arg(i)); 1419 } 1420 for (int i = 0; i < 50; i++) { 1421 createSimpleFile(dirW + QString("z_%1").arg(i)); 1422 } 1423 // Place some empty files with prefix Files in w. Note that / is missing. 1424 for (int i = 0; i < 50; i++) { 1425 createSimpleFile(dirW1 + QString("t_%1").arg(i)); 1426 } 1427 for (int i = 0; i < 50; i++) { 1428 createSimpleFile(dirW1 + QString("z_%1").arg(i)); 1429 } 1430 // Place some empty files with prefix Files in w/Files. Note that / is missing. 1431 for (int i = 0; i < 50; i++) { 1432 createSimpleFile(dirW2 + QString("t_%1").arg(i)); 1433 } 1434 for (int i = 0; i < 50; i++) { 1435 createSimpleFile(dirW2 + QString("z_%1").arg(i)); 1436 } 1437 // Listen to the w directory 1438 m_dirLister.openUrl(QUrl::fromLocalFile(dirW), KDirLister::NoFlags); 1439 1440 // Try to reproduce the bug #401552 renaming the w directory several times if needed 1441 const QStringList dirs = {dirW + "___", dirW + QLatin1Char('_'), dirW + "______", dirW + "_c", dirW + "___", dirW + "_________"}; 1442 1443 QString currDir = dirW; 1444 KIO::SimpleJob *job = nullptr; 1445 // Connect the redirection to openURL, so that on a rename the new location is opened. 1446 connect(&m_dirLister, &KCoreDirLister::redirection, this, [this](const QUrl &, const QUrl &newUrl) { 1447 slotOpenUrlOnRename(newUrl); 1448 }); 1449 1450 for (int i = 0; i < dirs.size(); i++) { 1451 // Wait for the listener to get all files 1452 QTRY_VERIFY(m_dirLister.isFinished()); 1453 // Do the rename 1454 QString newDir = dirs.at(i); 1455 job = KIO::rename(QUrl::fromLocalFile(currDir), QUrl::fromLocalFile(newDir), KIO::HideProgressInfo); 1456 QVERIFY2(job->exec(), qPrintable(job->errorString())); 1457 QTest::qWait(500); // Without the delay the crash doesn't happen 1458 currDir = newDir; 1459 } 1460 disconnect(&m_dirLister, nullptr, this, nullptr); 1461 } 1462 1463 void KDirListerTest::testRequestMimeType() 1464 { 1465 // Use a new tempdir and lister instance for this test, so that we don't use any cache at all. 1466 QTemporaryDir tempDir(homeTmpDir()); 1467 QString path = tempDir.path() + QLatin1Char('/'); 1468 1469 createTestFile(path + "/file_1"); 1470 createTestFile(path + "/file_2.txt"); 1471 createTestFile(path + "/file_3.cpp"); 1472 createTestFile(path + "/file_3.md"); 1473 1474 MyDirLister lister; 1475 // Explicitly set requestMimeTypeWhileListing to false so we know what state 1476 // it is in. 1477 lister.setRequestMimeTypeWhileListing(false); 1478 lister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 1479 1480 QTRY_VERIFY(lister.isFinished()); 1481 1482 auto items = lister.items(); 1483 for (auto item : std::as_const(items)) { 1484 QVERIFY(!item.isMimeTypeKnown()); 1485 } 1486 1487 // Verify that the mime types are what we expect them to be 1488 QCOMPARE(items[0].mimetype(), QStringLiteral("application/octet-stream")); 1489 QCOMPARE(items[1].mimetype(), QStringLiteral("text/plain")); 1490 QCOMPARE(items[2].mimetype(), QStringLiteral("text/x-c++src")); 1491 QCOMPARE(items[3].mimetype(), QStringLiteral("text/markdown")); 1492 1493 lister.setRequestMimeTypeWhileListing(true); 1494 lister.openUrl(QUrl::fromLocalFile(path), KDirLister::Reload); 1495 1496 QTRY_VERIFY(lister.isFinished()); 1497 1498 // If requestMimeTypeWhileListing is on, we should know the mime type of 1499 // items when they have been listed. 1500 items = lister.items(); 1501 for (auto item : std::as_const(items)) { 1502 QVERIFY(item.isMimeTypeKnown()); 1503 } 1504 1505 // Verify that the mime types are what we expect them to be 1506 QCOMPARE(items[0].mimetype(), QStringLiteral("application/octet-stream")); 1507 QCOMPARE(items[1].mimetype(), QStringLiteral("text/plain")); 1508 QCOMPARE(items[2].mimetype(), QStringLiteral("text/x-c++src")); 1509 QCOMPARE(items[3].mimetype(), QStringLiteral("text/markdown")); 1510 } 1511 1512 void KDirListerTest::testMimeFilter_data() 1513 { 1514 QTest::addColumn<QStringList>("files"); 1515 QTest::addColumn<QStringList>("mimeTypes"); 1516 QTest::addColumn<QStringList>("filteredFiles"); 1517 1518 const QStringList files = {"bla.txt", "main.cpp", "main.c", "image.jpeg"}; 1519 1520 QTest::newRow("single_file_exact_mimetype") << files << QStringList{"text/x-c++src"} << QStringList{"main.cpp"}; 1521 QTest::newRow("inherited_mimetype") << files << QStringList{"text/plain"} << QStringList{"bla.txt", "main.cpp", "main.c"}; 1522 QTest::newRow("no_match") << files << QStringList{"audio/flac"} << QStringList{}; 1523 } 1524 1525 void KDirListerTest::testMimeFilter() 1526 { 1527 // Use a new tempdir and lister instance for this test, so that we don't use any cache at all. 1528 QTemporaryDir tempDir(homeTmpDir()); 1529 QString path = tempDir.path() + '/'; 1530 1531 QFETCH(QStringList, files); 1532 QFETCH(QStringList, mimeTypes); 1533 QFETCH(QStringList, filteredFiles); 1534 1535 for (const QString &fileName : files) { 1536 createTestFile(path + fileName); 1537 } 1538 1539 MyDirLister lister; 1540 lister.setMimeFilter(mimeTypes); 1541 lister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 1542 1543 QSignalSpy spyCompleted(&lister, qOverload<>(&KCoreDirLister::completed)); 1544 QVERIFY(spyCompleted.wait(1000)); 1545 1546 QCOMPARE(lister.items().size(), filteredFiles.size()); 1547 1548 const auto items = lister.items(); 1549 for (const auto &item : items) { 1550 QVERIFY(filteredFiles.indexOf(item.name()) != -1); 1551 } 1552 } 1553 1554 void KDirListerTest::testDeleteCurrentDir() 1555 { 1556 // ensure m_dirLister holds the items. 1557 m_dirLister.openUrl(QUrl::fromLocalFile(tempPath()), KDirLister::NoFlags); 1558 m_dirLister.clearSpies(); 1559 KIO::DeleteJob *job = KIO::del(QUrl::fromLocalFile(tempPath()), KIO::HideProgressInfo); 1560 bool ok = job->exec(); 1561 QVERIFY(ok); 1562 QTRY_COMPARE(m_dirLister.spyClear.count(), 1); 1563 1564 QCOMPARE(m_dirLister.spyClearDir.count(), 0); 1565 1566 QList<QUrl> deletedUrls; 1567 for (int i = 0; i < m_dirLister.spyItemsDeleted.count(); ++i) { 1568 deletedUrls += m_dirLister.spyItemsDeleted[i][0].value<KFileItemList>().urlList(); 1569 } 1570 // qDebug() << deletedUrls; 1571 QUrl currentDirUrl = QUrl::fromLocalFile(tempPath()).adjusted(QUrl::StripTrailingSlash); 1572 // Sometimes I get ("current/subdir", "current") here, but that seems ok. 1573 QVERIFY(deletedUrls.contains(currentDirUrl)); 1574 } 1575 1576 void KDirListerTest::testForgetDir() 1577 { 1578 QTemporaryDir tempDir(homeTmpDir()); 1579 QString path = tempDir.path(); 1580 createTestFile(path + "/file_1"); 1581 1582 m_dirLister.openUrl(QUrl::fromLocalFile(path), KDirLister::Keep); 1583 QVERIFY(m_dirLister.spyCompleted.wait()); 1584 1585 m_dirLister.forgetDirs(QUrl::fromLocalFile(path)); 1586 1587 QSignalSpy addedSpy(&m_dirLister, &MyDirLister::itemsAdded); 1588 createTestFile(path + "/file_2"); 1589 QVERIFY(!addedSpy.wait(1000)); // to allow for KDirWatch's internal 500ms timer 1590 } 1591 1592 int KDirListerTest::fileCount() const 1593 { 1594 return QDir(tempPath()).entryList(QDir::AllEntries | QDir::NoDotAndDotDot).count(); 1595 } 1596 1597 void KDirListerTest::createSimpleFile(const QString &fileName) 1598 { 1599 QFile file(fileName); 1600 QVERIFY(file.open(QIODevice::WriteOnly)); 1601 file.write(QByteArray("foo")); 1602 file.close(); 1603 } 1604 1605 void KDirListerTest::fillDirLister2(MyDirLister &lister, const QString &path) 1606 { 1607 m_items2.clear(); 1608 connect(&lister, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems2); 1609 connect(&m_dirLister, &KCoreDirLister::refreshItems, this, &KDirListerTest::slotRefreshItems2); 1610 lister.openUrl(QUrl::fromLocalFile(path), KDirLister::NoFlags); 1611 QTRY_VERIFY(lister.isFinished()); 1612 } 1613 1614 void KDirListerTest::waitUntilMTimeChange(const QString &path) 1615 { 1616 // Wait until the current second is more than the file's mtime 1617 // otherwise this change will go unnoticed 1618 1619 QFileInfo fi(path); 1620 QVERIFY(fi.exists()); 1621 const QDateTime mtime = fi.lastModified(); 1622 waitUntilAfter(mtime); 1623 } 1624 1625 void KDirListerTest::waitUntilAfter(const QDateTime &ctime) 1626 { 1627 int totalWait = 0; 1628 QDateTime now; 1629 Q_FOREVER { 1630 now = QDateTime::currentDateTime(); 1631 if (now.toSecsSinceEpoch() == ctime.toSecsSinceEpoch()) { // truncate milliseconds 1632 totalWait += 50; 1633 QTest::qWait(50); 1634 } else { 1635 QVERIFY(now > ctime); // can't go back in time ;) 1636 QTest::qWait(50); // be safe 1637 break; 1638 } 1639 } 1640 // if (totalWait > 0) 1641 qDebug() << "Waited" << totalWait << "ms so that now" << now.toString(Qt::ISODate) << "is >" << ctime.toString(Qt::ISODate); 1642 } 1643 1644 // A bug in the decAutoUpdate/incAutoUpdate logic made KDirLister stop watching a directory for changes, 1645 // and stop watching a directory because a separate lister left a directory open in another lister 1646 void KDirListerTest::testBug386763() 1647 { 1648 QTemporaryDir newDir(homeTmpDir()); 1649 const QString path = newDir.path() + "/newsubdir/"; 1650 const QString otherpath = newDir.path() + "/othersubdir/"; 1651 1652 QDir().mkdir(path); 1653 MyDirLister dirLister; 1654 dirLister.openUrl(QUrl::fromLocalFile(path)); 1655 1656 // second lister opening same dir 1657 MyDirLister dirLister2; 1658 dirLister2.openUrl(QUrl::fromLocalFile(path)); 1659 QCOMPARE(dirLister2.spyCompleted.count(), 0); 1660 1661 connect(&dirLister2, &KCoreDirLister::newItems, this, &KDirListerTest::slotNewItems); 1662 QVERIFY(dirLister.spyCompleted.wait(500)); 1663 QVERIFY(dirLister.isFinished()); 1664 QVERIFY(m_items.isEmpty()); 1665 1666 // first lister opening another dir 1667 dirLister.openUrl(QUrl::fromLocalFile(otherpath)); 1668 1669 // Create a file in 'newsubdir' while still opened in dirLister2 1670 // bug was that the watch on ’newsubdir’ when dirLister left this dir 1671 // eventhough dirLister2 is still listing it 1672 QCOMPARE(dirLister2.spyCompleted.count(), 1); 1673 createTestFile(path + "newFile-1"); 1674 1675 QTRY_COMPARE(m_items.count(), 1); 1676 QVERIFY(KDirWatch::self()->contains(path)); 1677 1678 dirLister2.openUrl(QUrl::fromLocalFile(otherpath)); 1679 // checks we still watch the old path when second lister leaves it as it should be now in cache 1680 QVERIFY(KDirWatch::self()->contains(path)); 1681 1682 newDir.remove(); 1683 } 1684 1685 void KDirListerTest::testCacheEviction() 1686 { 1687 QTemporaryDir newDir(homeTmpDir()); 1688 1689 MyDirLister dirLister; 1690 dirLister.openUrl(QUrl::fromLocalFile(newDir.path())); 1691 QVERIFY(dirLister.spyCompleted.wait(500)); 1692 QVERIFY(dirLister.isFinished()); 1693 QVERIFY(KDirWatch::self()->contains(newDir.path())); 1694 1695 for (int i = 0; i < 12; i++) { 1696 const QString newDirPath = newDir.path() + QString("dir_%1").arg(i); 1697 QVERIFY(QDir().mkdir(newDirPath)); 1698 1699 dirLister.openUrl(QUrl::fromLocalFile(newDirPath)); 1700 QVERIFY(dirLister.spyCompleted.wait(500)); 1701 QVERIFY(dirLister.isFinished()); 1702 QVERIFY(KDirWatch::self()->contains(newDirPath)); 1703 } 1704 1705 // watches were removed as the dirItem were evicted from cache 1706 QVERIFY(!KDirWatch::self()->contains(newDir.path())); 1707 QVERIFY(!KDirWatch::self()->contains(newDir.path() + QString("dir_0"))); 1708 QVERIFY(KDirWatch::self()->contains(newDir.path() + QString("dir_1"))); 1709 } 1710 1711 #include "moc_kdirlistertest.cpp"