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