File indexing completed on 2024-05-12 04:19:59

0001 /*
0002 Gwenview: an image viewer
0003 Copyright 2007 Aurélien Gâteau <agateau@kde.org>
0004 
0005 This program is free software; you can redistribute it and/or
0006 modify it under the terms of the GNU General Public License
0007 as published by the Free Software Foundation; either version 2
0008 of the License, or (at your option) any later version.
0009 
0010 This program is distributed in the hope that it will be useful,
0011 but WITHOUT ANY WARRANTY; without even the implied warranty of
0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013 GNU General Public License for more details.
0014 
0015 You should have received a copy of the GNU General Public License
0016 along with this program; if not, write to the Free Software
0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
0018 
0019 */
0020 #include "thumbnailprovidertest.h"
0021 
0022 // Qt
0023 #include <QDebug>
0024 #include <QDir>
0025 #include <QFile>
0026 #include <QImage>
0027 #include <QPainter>
0028 #include <QTest>
0029 
0030 // KF
0031 #include <KIO/CopyJob>
0032 #include <KIO/DeleteJob>
0033 
0034 // Local
0035 #include "../lib/thumbnailprovider/thumbnailprovider.h"
0036 #include "gwenviewconfig.h"
0037 #include "testutils.h"
0038 
0039 // libc
0040 #include <cerrno>
0041 #include <cstring>
0042 
0043 using namespace Gwenview;
0044 
0045 QTEST_MAIN(ThumbnailProviderTest)
0046 
0047 SandBox::SandBox()
0048     : mPath(QDir::currentPath() + "/sandbox")
0049 {
0050 }
0051 
0052 void SandBox::initDir()
0053 {
0054     KIO::Job *job;
0055     QDir dir(mPath);
0056     if (dir.exists()) {
0057         QUrl sandBoxUrl("file://" + mPath);
0058         job = KIO::del(sandBoxUrl);
0059         QVERIFY2(job->exec(), "Couldn't delete sandbox");
0060     }
0061     dir.mkpath(".");
0062 }
0063 
0064 void SandBox::fill()
0065 {
0066     initDir();
0067     createTestImage("red.png", 300, 200, Qt::red);
0068     createTestImage("blue.png", 200, 300, Qt::blue);
0069     createTestImage("small.png", 50, 50, Qt::green);
0070 
0071     copyTestImage("orient6.jpg", 128, 256);
0072     copyTestImage("orient6-small.jpg", 32, 64);
0073 }
0074 
0075 void SandBox::copyTestImage(const QString &testFileName, int width, int height)
0076 {
0077     QUrl testPath = urlForTestFile(testFileName);
0078     QUrl testDest = QUrl("file://" + mPath + '/' + testFileName);
0079     KIO::Job *job = KIO::copy(testPath, testDest);
0080     QVERIFY2(job->exec(), "Couldn't copy test image");
0081     mSizeHash.insert(testFileName, QSize(width, height));
0082 }
0083 
0084 static QImage createColoredImage(int width, int height, const QColor &color)
0085 {
0086     QImage image(width, height, QImage::Format_RGB32);
0087     QPainter painter(&image);
0088     painter.fillRect(image.rect(), color);
0089     return image;
0090 }
0091 
0092 void SandBox::createTestImage(const QString &name, int width, int height, const QColor &color)
0093 {
0094     QImage image = createColoredImage(width, height, color);
0095     image.save(mPath + '/' + name, "png");
0096     mSizeHash.insert(name, QSize(width, height));
0097 }
0098 
0099 void ThumbnailProviderTest::initTestCase()
0100 {
0101     qRegisterMetaType<KFileItem>("KFileItem");
0102 }
0103 
0104 void ThumbnailProviderTest::init()
0105 {
0106     ThumbnailProvider::setThumbnailBaseDir(mSandBox.mPath + "/thumbnails/");
0107     mSandBox.fill();
0108 }
0109 
0110 static void syncRun(ThumbnailProvider *provider)
0111 {
0112     QEventLoop loop;
0113     QObject::connect(provider, SIGNAL(finished()), &loop, SLOT(quit()));
0114     loop.exec();
0115 }
0116 
0117 void ThumbnailProviderTest::testLoadLocal()
0118 {
0119     QDir dir(mSandBox.mPath);
0120 
0121     // Create a list of items which will be thumbnailed
0122     KFileItemList list;
0123     const auto entryInfoList = dir.entryInfoList(QDir::Files);
0124     for (const QFileInfo &info : entryInfoList) {
0125         QUrl url("file://" + info.absoluteFilePath());
0126         KFileItem item(url);
0127         list << item;
0128     }
0129 
0130     // Generate the thumbnails
0131     ThumbnailProvider provider;
0132     provider.setThumbnailGroup(ThumbnailGroup::Normal);
0133     provider.appendItems(list);
0134     QSignalSpy spy(&provider, SIGNAL(thumbnailLoaded(KFileItem, QPixmap, QSize, qulonglong)));
0135     syncRun(&provider);
0136     while (!ThumbnailProvider::isThumbnailWriterEmpty()) {
0137         QTest::qWait(100);
0138     }
0139 
0140     // Check we generated the correct number of thumbnails
0141     QDir thumbnailDir = ThumbnailProvider::thumbnailBaseDir(ThumbnailGroup::Normal);
0142     // There should be one file less because small.png is a png and is too
0143     // small to have a thumbnail
0144     const QStringList entryList = thumbnailDir.entryList(QStringList("*.png"));
0145     QCOMPARE(entryList.count(), mSandBox.mSizeHash.size() - 1);
0146 
0147     // Check thumbnail keys
0148     QHash<KFileItem, QHash<QString, QString>> thumbnailHash;
0149     for (const QString &name : entryList) {
0150         QImage thumb;
0151         QVERIFY(thumb.load(thumbnailDir.filePath(name)));
0152 
0153         QUrl url(thumb.text("Thumb::URI"));
0154         KFileItem item = list.findByUrl(url);
0155         QVERIFY(!item.isNull());
0156 
0157         QSize originalSize = mSandBox.mSizeHash.value(item.url().fileName());
0158         uint mtime = item.time(KFileItem::ModificationTime).toSecsSinceEpoch();
0159 
0160         if (mtime == uint(-1)) {
0161             // This happens from time to time on build.kde.org, but I haven't
0162             // been able to reproduce it locally, so let's try to gather more
0163             // information.
0164             qWarning() << "mtime == -1 for url" << url << ". This should not happen!";
0165             qWarning() << "errno:" << errno << "message:" << strerror(errno);
0166             qWarning() << "QFile::exists(" << url.toLocalFile() << "):" << QFile::exists(url.toLocalFile());
0167             qWarning() << "Recalculating mtime" << item.time(KFileItem::ModificationTime).toSecsSinceEpoch();
0168             QFAIL("Invalid time for test KFileItem");
0169         }
0170 
0171         QCOMPARE(thumb.text("Thumb::Image::Width"), QString::number(originalSize.width()));
0172         QCOMPARE(thumb.text("Thumb::Image::Height"), QString::number(originalSize.height()));
0173         QCOMPARE(thumb.text("Thumb::Mimetype"), item.mimetype());
0174         QCOMPARE(thumb.text("Thumb::Size"), QString::number(item.size()));
0175         QCOMPARE(thumb.text("Thumb::MTime"), QString::number(mtime));
0176     }
0177 
0178     // Check what was in the thumbnailLoaded() signals
0179     QCOMPARE(spy.count(), mSandBox.mSizeHash.size());
0180     QSignalSpy::ConstIterator it = spy.constBegin(), end = spy.constEnd();
0181     for (; it != end; ++it) {
0182         const QVariantList args = *it;
0183         const KFileItem item = qvariant_cast<KFileItem>(args.at(0));
0184         const QSize size = args.at(2).toSize();
0185         const QSize expectedSize = mSandBox.mSizeHash.value(item.url().fileName());
0186         QCOMPARE(size, expectedSize);
0187     }
0188 }
0189 
0190 void ThumbnailProviderTest::testUseEmbeddedOrNot()
0191 {
0192     QImage expectedThumbnail;
0193     QPixmap thumbnailPix;
0194     SandBox sandBox;
0195     sandBox.initDir();
0196     // This image is red (0xfe0000) and 256x128 but contains a white 128x64 thumbnail
0197     sandBox.copyTestImage("embedded-thumbnail.jpg", 256, 128);
0198 
0199     KFileItemList list;
0200     QUrl url("file://" + QDir(sandBox.mPath).absoluteFilePath("embedded-thumbnail.jpg"));
0201     list << KFileItem(url);
0202 
0203     // Loading a normal thumbnail should bring the white one
0204     {
0205         ThumbnailProvider provider;
0206         provider.setThumbnailGroup(ThumbnailGroup::Normal);
0207         provider.appendItems(list);
0208         QSignalSpy spy(&provider, SIGNAL(thumbnailLoaded(KFileItem, QPixmap, QSize, qulonglong)));
0209         syncRun(&provider);
0210 
0211         QCOMPARE(spy.count(), 1);
0212         expectedThumbnail = createColoredImage(128, 64, Qt::white);
0213         thumbnailPix = qvariant_cast<QPixmap>(spy.at(0).at(1));
0214         QVERIFY(TestUtils::imageCompare(expectedThumbnail, thumbnailPix.toImage()));
0215     }
0216 
0217     // Loading a large thumbnail should bring the red one, unless thumbnails are deleted on exit,
0218     // which should bring the white one
0219     {
0220         ThumbnailProvider provider;
0221         provider.setThumbnailGroup(ThumbnailGroup::Large);
0222         provider.appendItems(list);
0223         QSignalSpy spy(&provider, SIGNAL(thumbnailLoaded(KFileItem, QPixmap, QSize, qulonglong)));
0224         syncRun(&provider);
0225 
0226         QCOMPARE(spy.count(), 1);
0227         if (GwenviewConfig::lowResourceUsageMode()) {
0228             expectedThumbnail = createColoredImage(128, 64, Qt::white);
0229         } else {
0230             expectedThumbnail = createColoredImage(256, 128, QColor(254, 0, 0));
0231         }
0232         thumbnailPix = qvariant_cast<QPixmap>(spy.at(0).at(1));
0233         QVERIFY(TestUtils::imageCompare(expectedThumbnail, thumbnailPix.toImage()));
0234     }
0235 }
0236 
0237 void ThumbnailProviderTest::testLoadRemote()
0238 {
0239     QUrl url = setUpRemoteTestDir("test.png");
0240     if (!url.isValid()) {
0241         QSKIP("Not running this test: failed to setup remote test dir.");
0242     }
0243     url = url.adjusted(QUrl::StripTrailingSlash);
0244     url.setPath(url.path() + '/' + "test.png");
0245 
0246     KFileItemList list;
0247     KFileItem item(url);
0248     list << item;
0249 
0250     ThumbnailProvider provider;
0251     provider.setThumbnailGroup(ThumbnailGroup::Normal);
0252     provider.appendItems(list);
0253     syncRun(&provider);
0254     while (!ThumbnailProvider::isThumbnailWriterEmpty()) {
0255         QTest::qWait(100);
0256     }
0257 
0258     QDir thumbnailDir = ThumbnailProvider::thumbnailBaseDir(ThumbnailGroup::Normal);
0259     QStringList entryList = thumbnailDir.entryList(QStringList("*.png"));
0260     QCOMPARE(entryList.count(), 1);
0261 }
0262 
0263 void ThumbnailProviderTest::testRemoveItemsWhileGenerating()
0264 {
0265     QDir dir(mSandBox.mPath);
0266 
0267     // Create a list of items which will be thumbnailed
0268     KFileItemList list;
0269     const auto entryInfoList = dir.entryInfoList(QDir::Files);
0270     for (const QFileInfo &info : entryInfoList) {
0271         QUrl url("file://" + info.absoluteFilePath());
0272         KFileItem item(url);
0273         list << item;
0274     }
0275 
0276     // Start generating thumbnails for items
0277     ThumbnailProvider provider;
0278     provider.setThumbnailGroup(ThumbnailGroup::Normal);
0279     provider.appendItems(list);
0280     QEventLoop loop;
0281     connect(&provider, SIGNAL(finished()), &loop, SLOT(quit()));
0282 
0283     // Remove items, it should not crash
0284     provider.removeItems(list);
0285     loop.exec();
0286 }
0287 
0288 #include "moc_thumbnailprovidertest.cpp"