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