Files
RedBear-OS/local/recipes/kde/kf6-kio/source/autotests/favicontest.cpp
T
2026-04-14 10:51:06 +01:00

293 lines
9.7 KiB
C++

/*
This file is part of KDE
SPDX-FileCopyrightText: 2006-2016 David Faure <faure@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <KIO/FavIconRequestJob>
#include <KIO/TransferJob>
#include <QDir>
#include <QLoggingCategory>
#include <QStandardPaths>
#include <QTest>
#include <QFutureSynchronizer>
#include <QThreadPool>
#include <QtConcurrentRun>
static const char s_hostUrl[] = "http://www.google.com/index.html";
static const char s_pageUrl[] = "http://www.google.com/somepage.html";
static const char s_iconUrl[] = "http://www.google.com/favicon.ico";
static const char s_altIconUrl[] = "http://www.ibm.com/favicon.ico";
static const char s_thirdIconUrl[] = "http://www.google.fr/favicon.ico";
static const char s_iconUrlForThreadTest[] = "http://www.google.de/favicon.ico";
static enum NetworkAccess {
Unknown,
Yes,
No
} s_networkAccess = Unknown;
static bool checkNetworkAccess()
{
if (s_networkAccess == Unknown) {
QElapsedTimer tm;
tm.start();
KIO::Job *job = KIO::get(QUrl(s_iconUrl), KIO::NoReload, KIO::HideProgressInfo);
if (job->exec()) {
s_networkAccess = Yes;
qDebug() << "Network access OK. Download time" << tm.elapsed() << "ms";
} else {
qWarning() << job->errorString();
s_networkAccess = No;
}
}
return s_networkAccess == Yes;
}
class FavIconTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void favIconForUrlShouldBeEmptyInitially();
void hostJobShouldDownloadIconThenUseCache();
void iconUrlJobShouldDownloadIconThenUseCache();
void reloadShouldReload();
void failedDownloadShouldBeRemembered();
void tooBigFaviconShouldAbort();
void simultaneousRequestsShouldWork();
void concurrentRequestsShouldWork();
private:
};
void FavIconTest::initTestCase()
{
QStandardPaths::setTestModeEnabled(true);
// To let ctest exit, we shouldn't start kio_http_cache_cleaner
qputenv("KIO_DISABLE_CACHE_CLEANER", "yes");
// To get KJob::errorString() in English
qputenv("LC_ALL", "en_US.UTF-8");
qputenv("LANGUAGE", "en");
if (!checkNetworkAccess()) {
QSKIP("no network access", SkipAll);
}
// Ensure we start with no cache on disk
const QString favIconCacheDir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/favicons");
QDir(favIconCacheDir).removeRecursively();
QVERIFY(!QFileInfo::exists(favIconCacheDir));
// Enable debug output
QLoggingCategory::setFilterRules(QStringLiteral("kf.kio.gui.favicons.debug=true"));
}
void FavIconTest::favIconForUrlShouldBeEmptyInitially()
{
QCOMPARE(KIO::favIconForUrl(QUrl(s_hostUrl)), QString());
}
// Waits for start() and checks whether a transfer job was created.
static bool willDownload(KIO::FavIconRequestJob *job)
{
qApp->sendPostedEvents(job, QEvent::MetaCall); // start() is delayed
return job->findChild<KIO::TransferJob *>();
}
void FavIconTest::hostJobShouldDownloadIconThenUseCache()
{
const QUrl url(s_hostUrl);
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
QVERIFY(willDownload(job));
QVERIFY(job->exec());
const QString iconFile = job->iconFile();
QVERIFY(iconFile.endsWith(QLatin1String("favicons/www.google.com.png")));
QVERIFY2(QFile::exists(iconFile), qPrintable(iconFile));
QVERIFY(!QIcon(iconFile).isNull()); // pass full path to QIcon
// This requires https://codereview.qt-project.org/148444
// QVERIFY(!QIcon::fromTheme(iconFile).isNull()); // old code ported from kdelibs4 might do that, should work too
// Lookup should give the same result
QCOMPARE(KIO::favIconForUrl(url), iconFile);
// Second job should use the cache
KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url);
QVERIFY(!willDownload(secondJob));
QVERIFY(secondJob->exec());
QCOMPARE(secondJob->iconFile(), iconFile);
// The code from the class docu
QString goticonFile;
{
KIO::FavIconRequestJob *favJob = new KIO::FavIconRequestJob(url);
connect(favJob, &KIO::FavIconRequestJob::result, this, [favJob, &goticonFile](KJob *) {
if (!favJob->error()) {
goticonFile = favJob->iconFile();
}
});
QVERIFY(favJob->exec());
}
QCOMPARE(goticonFile, iconFile);
}
void FavIconTest::iconUrlJobShouldDownloadIconThenUseCache()
{
const QUrl url(s_pageUrl);
// Set icon URL to "ibm"
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
job->setIconUrl(QUrl(s_altIconUrl));
QVERIFY(willDownload(job));
QVERIFY(job->exec());
const QString iconFile = job->iconFile();
QVERIFY(iconFile.endsWith(QLatin1String("favicons/www.ibm.com.png")));
QVERIFY2(QFile::exists(iconFile), qPrintable(iconFile));
QVERIFY(!QPixmap(iconFile).isNull()); // pass full path to QPixmap (to test this too)
// Lookup should give the same result
QCOMPARE(KIO::favIconForUrl(url), iconFile);
// Second job should use the cache. It doesn't even need the icon url again.
KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url);
QVERIFY(!willDownload(secondJob));
QVERIFY(secondJob->exec());
QCOMPARE(secondJob->iconFile(), iconFile);
// Set icon URL to "www.google.fr/favicon.ico"
KIO::FavIconRequestJob *thirdJob = new KIO::FavIconRequestJob(url);
thirdJob->setIconUrl(QUrl(s_thirdIconUrl));
QVERIFY(willDownload(thirdJob));
QVERIFY(thirdJob->exec());
const QString newiconFile = thirdJob->iconFile();
QVERIFY(newiconFile.endsWith(QLatin1String("favicons/www.google.fr.png")));
// Lookup should give the same result
QCOMPARE(KIO::favIconForUrl(url), newiconFile);
}
void FavIconTest::reloadShouldReload()
{
const QUrl url(s_hostUrl);
// First job, to make sure it's in the cache (if the other tests didn't run first)
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
QVERIFY(job->exec());
const QString iconFile = job->iconFile();
// Second job should use the cache
KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url);
QVERIFY(!willDownload(secondJob));
QVERIFY(secondJob->exec());
QCOMPARE(secondJob->iconFile(), iconFile);
// job with Reload should not use the cache
KIO::FavIconRequestJob *jobWithReload = new KIO::FavIconRequestJob(url, KIO::Reload);
QVERIFY(willDownload(jobWithReload));
QVERIFY(jobWithReload->exec());
QCOMPARE(jobWithReload->iconFile(), iconFile);
}
void FavIconTest::failedDownloadShouldBeRemembered()
{
const QUrl url(s_pageUrl);
// Set icon URL to a non-existing favicon
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
job->setIconUrl(QUrl("https://kde.org/doesnotexist/favicon.ico"));
QVERIFY(willDownload(job));
QVERIFY(!job->exec());
QVERIFY(job->iconFile().isEmpty());
qDebug() << job->errorString();
QCOMPARE(job->error(), int(KIO::ERR_DOES_NOT_EXIST));
QCOMPARE(job->errorString(), QStringLiteral("The file or folder https://kde.org/doesnotexist/favicon.ico does not exist."));
// Second job should use the cache and not do anything
KIO::FavIconRequestJob *secondJob = new KIO::FavIconRequestJob(url);
QVERIFY(!willDownload(secondJob));
QVERIFY(!secondJob->exec());
QVERIFY(secondJob->iconFile().isEmpty());
QCOMPARE(job->error(), int(KIO::ERR_DOES_NOT_EXIST));
QCOMPARE(job->errorString(), QStringLiteral("The file or folder https://kde.org/doesnotexist/favicon.ico does not exist."));
}
void FavIconTest::tooBigFaviconShouldAbort()
{
const QUrl url(s_pageUrl);
// Set icon URL to a >65KB file
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
job->setIconUrl(QUrl("http://download.kde.org/Attic/4.13.2/src/kcalc-4.13.2.tar.xz"));
QVERIFY(willDownload(job));
QVERIFY(!job->exec());
QCOMPARE(job->error(), int(KIO::ERR_WORKER_DEFINED));
QCOMPARE(job->errorString(), QStringLiteral("Icon file too big, download aborted"));
}
void FavIconTest::simultaneousRequestsShouldWork()
{
const QUrl url(s_hostUrl);
// First job, to find out the iconFile and delete it
QString iconFile;
{
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
QVERIFY(job->exec());
iconFile = job->iconFile();
QFile::remove(iconFile);
}
// This is a case we could maybe optimize: not downloading twice in parallel
KIO::FavIconRequestJob *job1 = new KIO::FavIconRequestJob(url);
job1->setAutoDelete(false);
KIO::FavIconRequestJob *job2 = new KIO::FavIconRequestJob(url);
job2->setAutoDelete(false);
QVERIFY(willDownload(job1));
QVERIFY(willDownload(job2));
QVERIFY(job1->exec());
QCOMPARE(job1->iconFile(), iconFile);
QVERIFY(job2->exec());
QCOMPARE(job2->iconFile(), iconFile);
delete job1;
delete job2;
}
static QString getAltIconUrl()
{
const QUrl url(s_pageUrl);
// Set icon URL to one that we haven't downloaded yet
KIO::FavIconRequestJob *job = new KIO::FavIconRequestJob(url);
job->setIconUrl(QUrl(s_iconUrlForThreadTest));
job->exec();
return job->iconFile();
}
void FavIconTest::concurrentRequestsShouldWork()
{
const int numThreads = 3;
QThreadPool tp;
tp.setMaxThreadCount(numThreads);
QList<QFuture<QString>> futures(numThreads);
for (int i = 0; i < numThreads; ++i) {
futures[i] = QtConcurrent::run(&tp, getAltIconUrl);
}
QVERIFY(tp.waitForDone(60000));
const QString firstResult = futures.at(0).result();
for (int i = 1; i < numThreads; ++i) {
QCOMPARE(futures.at(i).result(), firstResult);
}
QVERIFY(!QPixmap(firstResult).isNull());
}
QTEST_MAIN(FavIconTest)
#include "favicontest.moc"