File indexing completed on 2025-01-12 12:29:23
0001 /* 0002 This file is part of KDE 0003 SPDX-FileCopyrightText: 2006-2016 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include <KIO/FavIconRequestJob> 0009 #include <KIO/Job> 0010 #include <QDir> 0011 #include <QLoggingCategory> 0012 #include <QStandardPaths> 0013 #include <QTest> 0014 0015 #include <QFutureSynchronizer> 0016 #include <QThreadPool> 0017 #include <QtConcurrentRun> 0018 0019 static const char s_hostUrl[] = "http://www.google.com/index.html"; 0020 static const char s_pageUrl[] = "http://www.google.com/somepage.html"; 0021 static const char s_iconUrl[] = "http://www.google.com/favicon.ico"; 0022 static const char s_altIconUrl[] = "http://www.ibm.com/favicon.ico"; 0023 static const char s_thirdIconUrl[] = "http://www.google.fr/favicon.ico"; 0024 static const char s_iconUrlForThreadTest[] = "http://www.google.de/favicon.ico"; 0025 0026 static enum NetworkAccess { Unknown, Yes, No } s_networkAccess = Unknown; 0027 static bool checkNetworkAccess() 0028 { 0029 if (s_networkAccess == Unknown) { 0030 QElapsedTimer tm; 0031 tm.start(); 0032 KIO::Job *job = KIO::get(QUrl(s_iconUrl), KIO::NoReload, KIO::HideProgressInfo); 0033 if (job->exec()) { 0034 s_networkAccess = Yes; 0035 qDebug() << "Network access OK. Download time" << tm.elapsed() << "ms"; 0036 } else { 0037 qWarning() << job->errorString(); 0038 s_networkAccess = No; 0039 } 0040 } 0041 return s_networkAccess == Yes; 0042 } 0043 0044 class FavIconTest : public QObject 0045 { 0046 Q_OBJECT 0047 0048 private Q_SLOTS: 0049 void initTestCase(); 0050 void favIconForUrlShouldBeEmptyInitially(); 0051 void hostJobShouldDownloadIconThenUseCache(); 0052 void iconUrlJobShouldDownloadIconThenUseCache(); 0053 void reloadShouldReload(); 0054 void failedDownloadShouldBeRemembered(); 0055 void tooBigFaviconShouldAbort(); 0056 void simultaneousRequestsShouldWork(); 0057 void concurrentRequestsShouldWork(); 0058 0059 private: 0060 }; 0061 0062 void FavIconTest::initTestCase() 0063 { 0064 QStandardPaths::setTestModeEnabled(true); 0065 0066 // To let ctest exit, we shouldn't start kio_http_cache_cleaner 0067 qputenv("KIO_DISABLE_CACHE_CLEANER", "yes"); 0068 // To get KJob::errorString() in English 0069 qputenv("LC_ALL", "en_US.UTF-8"); 0070 0071 if (!checkNetworkAccess()) { 0072 QSKIP("no network access", SkipAll); 0073 } 0074 0075 // Ensure we start with no cache on disk 0076 const QString favIconCacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/favicons"); 0077 QDir(favIconCacheDir).removeRecursively(); 0078 QVERIFY(!QFileInfo::exists(favIconCacheDir)); 0079 0080 // Enable debug output 0081 QLoggingCategory::setFilterRules(QStringLiteral("kf.kio.gui.favicons.debug=true")); 0082 } 0083 0084 void FavIconTest::favIconForUrlShouldBeEmptyInitially() 0085 { 0086 QCOMPARE(KIO::favIconForUrl(QUrl(s_hostUrl)), QString()); 0087 } 0088 0089 // Waits for start() and checks whether a transfer job was created. 0090 static bool willDownload(KIO::FavIconRequestJob *job) 0091 { 0092 qApp->sendPostedEvents(job, QEvent::MetaCall); // start() is delayed 0093 return job->findChild<KIO::TransferJob *>(); 0094 } 0095 0096 void FavIconTest::hostJobShouldDownloadIconThenUseCache() 0097 { 0098 const QUrl url(s_hostUrl); 0099 0100 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0101 QVERIFY(willDownload(job)); 0102 QVERIFY(job->exec()); 0103 const QString iconFile = job->iconFile(); 0104 QVERIFY(iconFile.endsWith(QLatin1String("favicons/www.google.com.png"))); 0105 QVERIFY2(QFile::exists(iconFile), qPrintable(iconFile)); 0106 QVERIFY(!QIcon(iconFile).isNull()); // pass full path to QIcon 0107 // This requires https://codereview.qt-project.org/148444 0108 // QVERIFY(!QIcon::fromTheme(iconFile).isNull()); // old code ported from kdelibs4 might do that, should work too 0109 0110 // Lookup should give the same result 0111 QCOMPARE(KIO::favIconForUrl(url), iconFile); 0112 0113 // Second job should use the cache 0114 KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url); 0115 QVERIFY(!willDownload(secondJob)); 0116 QVERIFY(secondJob->exec()); 0117 QCOMPARE(secondJob->iconFile(), iconFile); 0118 0119 // The code from the class docu 0120 QString goticonFile; 0121 { 0122 KIO::FavIconRequestJob *favJob = new KIO::FavIconRequestJob(url); 0123 connect(favJob, &KIO::FavIconRequestJob::result, this, [favJob, &goticonFile](KJob *) { 0124 if (!favJob->error()) { 0125 goticonFile = favJob->iconFile(); 0126 } 0127 }); 0128 QVERIFY(favJob->exec()); 0129 } 0130 QCOMPARE(goticonFile, iconFile); 0131 } 0132 0133 void FavIconTest::iconUrlJobShouldDownloadIconThenUseCache() 0134 { 0135 const QUrl url(s_pageUrl); 0136 0137 // Set icon URL to "ibm" 0138 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0139 job->setIconUrl(QUrl(s_altIconUrl)); 0140 QVERIFY(willDownload(job)); 0141 QVERIFY(job->exec()); 0142 const QString iconFile = job->iconFile(); 0143 QVERIFY(iconFile.endsWith(QLatin1String("favicons/www.ibm.com.png"))); 0144 QVERIFY2(QFile::exists(iconFile), qPrintable(iconFile)); 0145 QVERIFY(!QPixmap(iconFile).isNull()); // pass full path to QPixmap (to test this too) 0146 0147 // Lookup should give the same result 0148 QCOMPARE(KIO::favIconForUrl(url), iconFile); 0149 0150 // Second job should use the cache. It doesn't even need the icon url again. 0151 KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url); 0152 QVERIFY(!willDownload(secondJob)); 0153 QVERIFY(secondJob->exec()); 0154 QCOMPARE(secondJob->iconFile(), iconFile); 0155 0156 // Set icon URL to "www.google.fr/favicon.ico" 0157 KIO::FavIconRequestJob *thirdJob = new KIO::FavIconRequestJob(url); 0158 thirdJob->setIconUrl(QUrl(s_thirdIconUrl)); 0159 QVERIFY(willDownload(thirdJob)); 0160 QVERIFY(thirdJob->exec()); 0161 const QString newiconFile = thirdJob->iconFile(); 0162 QVERIFY(newiconFile.endsWith(QLatin1String("favicons/www.google.fr.png"))); 0163 0164 // Lookup should give the same result 0165 QCOMPARE(KIO::favIconForUrl(url), newiconFile); 0166 } 0167 0168 void FavIconTest::reloadShouldReload() 0169 { 0170 const QUrl url(s_hostUrl); 0171 0172 // First job, to make sure it's in the cache (if the other tests didn't run first) 0173 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0174 QVERIFY(job->exec()); 0175 const QString iconFile = job->iconFile(); 0176 0177 // Second job should use the cache 0178 KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url); 0179 QVERIFY(!willDownload(secondJob)); 0180 QVERIFY(secondJob->exec()); 0181 QCOMPARE(secondJob->iconFile(), iconFile); 0182 0183 // job with Reload should not use the cache 0184 KIO::FavIconRequestJob *jobWithReload = new KIO::FavIconRequestJob(url, KIO::Reload); 0185 QVERIFY(willDownload(jobWithReload)); 0186 QVERIFY(jobWithReload->exec()); 0187 QCOMPARE(jobWithReload->iconFile(), iconFile); 0188 } 0189 0190 void FavIconTest::failedDownloadShouldBeRemembered() 0191 { 0192 const QUrl url(s_pageUrl); 0193 0194 // Set icon URL to a non-existing favicon 0195 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0196 job->setIconUrl(QUrl("https://kde.org/doesnotexist/favicon.ico")); 0197 QVERIFY(willDownload(job)); 0198 QVERIFY(!job->exec()); 0199 QVERIFY(job->iconFile().isEmpty()); 0200 qDebug() << job->errorString(); 0201 QCOMPARE(job->error(), int(KIO::ERR_DOES_NOT_EXIST)); 0202 QCOMPARE(job->errorString(), QStringLiteral("The file or folder https://kde.org/doesnotexist/favicon.ico does not exist.")); 0203 0204 // Second job should use the cache and not do anything 0205 KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url); 0206 QVERIFY(!willDownload(secondJob)); 0207 QVERIFY(!secondJob->exec()); 0208 QVERIFY(secondJob->iconFile().isEmpty()); 0209 QCOMPARE(job->error(), int(KIO::ERR_DOES_NOT_EXIST)); 0210 QCOMPARE(job->errorString(), QStringLiteral("The file or folder https://kde.org/doesnotexist/favicon.ico does not exist.")); 0211 } 0212 0213 void FavIconTest::tooBigFaviconShouldAbort() 0214 { 0215 const QUrl url(s_pageUrl); 0216 0217 // Set icon URL to a >65KB file 0218 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0219 job->setIconUrl(QUrl("http://download.kde.org/Attic/4.13.2/src/kcalc-4.13.2.tar.xz")); 0220 QVERIFY(willDownload(job)); 0221 QVERIFY(!job->exec()); 0222 QCOMPARE(job->error(), int(KIO::ERR_WORKER_DEFINED)); 0223 QCOMPARE(job->errorString(), QStringLiteral("Icon file too big, download aborted")); 0224 } 0225 0226 void FavIconTest::simultaneousRequestsShouldWork() 0227 { 0228 const QUrl url(s_hostUrl); 0229 0230 // First job, to find out the iconFile and delete it 0231 QString iconFile; 0232 { 0233 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0234 QVERIFY(job->exec()); 0235 iconFile = job->iconFile(); 0236 QFile::remove(iconFile); 0237 } 0238 0239 // This is a case we could maybe optimize: not downloading twice in parallel 0240 KIO::FavIconRequestJob *job1 = new KIO::FavIconRequestJob(url); 0241 job1->setAutoDelete(false); 0242 KIO::FavIconRequestJob *job2 = new KIO::FavIconRequestJob(url); 0243 job2->setAutoDelete(false); 0244 QVERIFY(willDownload(job1)); 0245 QVERIFY(willDownload(job2)); 0246 0247 QVERIFY(job1->exec()); 0248 QCOMPARE(job1->iconFile(), iconFile); 0249 0250 QVERIFY(job2->exec()); 0251 QCOMPARE(job2->iconFile(), iconFile); 0252 0253 delete job1; 0254 delete job2; 0255 } 0256 0257 static QString getAltIconUrl() 0258 { 0259 const QUrl url(s_pageUrl); 0260 // Set icon URL to one that we haven't downloaded yet 0261 KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url); 0262 job->setIconUrl(QUrl(s_iconUrlForThreadTest)); 0263 job->exec(); 0264 return job->iconFile(); 0265 } 0266 0267 void FavIconTest::concurrentRequestsShouldWork() 0268 { 0269 const int numThreads = 3; 0270 QThreadPool tp; 0271 tp.setMaxThreadCount(numThreads); 0272 QVector<QFuture<QString>> futures(numThreads); 0273 for (int i = 0; i < numThreads; ++i) { 0274 futures[i] = QtConcurrent::run(&tp, getAltIconUrl); 0275 } 0276 QVERIFY(tp.waitForDone(60000)); 0277 0278 const QString firstResult = futures.at(0).result(); 0279 for (int i = 1; i < numThreads; ++i) { 0280 QCOMPARE(futures.at(i).result(), firstResult); 0281 } 0282 QVERIFY(!QPixmap(firstResult).isNull()); 0283 } 0284 0285 QTEST_MAIN(FavIconTest) 0286 0287 #include "favicontest.moc"