File indexing completed on 2024-05-05 12:17:23

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2008 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <kiconloader.h>
0009 
0010 #include <QDir>
0011 #include <QRegularExpression>
0012 #include <QStandardPaths>
0013 #include <QTest>
0014 
0015 #include <KPixmapSequence>
0016 
0017 #include <KConfigGroup>
0018 #include <KSharedConfig>
0019 
0020 extern KICONTHEMES_EXPORT void uintToHex(uint32_t colorData, QChar *buffer);
0021 
0022 class KIconLoader_UnitTest : public QObject
0023 {
0024     Q_OBJECT
0025 public:
0026     KIconLoader_UnitTest()
0027         : testSizes({12, 22, 32, 42, 82, 132, 243})
0028     {
0029     }
0030 
0031 private:
0032     QDir testDataDir;
0033     QDir testIconsDir;
0034     QString appName;
0035     QDir appDataDir;
0036     const QVector<int> testSizes;
0037 
0038 private Q_SLOTS:
0039     void initTestCase()
0040     {
0041         QStandardPaths::setTestModeEnabled(true);
0042 
0043         const QStringList genericIconsFiles = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("mime/generic-icons"));
0044         QVERIFY(!genericIconsFiles.isEmpty()); // KIconLoader relies on fallbacks to generic icons (e.g. x-office-document), which comes from a shared-mime-info
0045                                                // file. Make sure it's installed!
0046 
0047         KConfigGroup cg(KSharedConfig::openConfig(), "Icons");
0048         cg.writeEntry("Theme", "breeze");
0049         cg.sync();
0050 
0051         testDataDir = QDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
0052         testIconsDir = QDir(testDataDir.absoluteFilePath(QStringLiteral("icons")));
0053 
0054         appName = QStringLiteral("kiconloader_unittest");
0055         appDataDir = QDir(testDataDir.absoluteFilePath(appName));
0056 
0057         // we will be recursively deleting these, so a sanity check is in order
0058         QVERIFY(testIconsDir.absolutePath().contains(QLatin1String("qttest")));
0059         QVERIFY(appDataDir.absolutePath().contains(QLatin1String("qttest")));
0060 
0061         testIconsDir.removeRecursively();
0062         appDataDir.removeRecursively();
0063 
0064         QVERIFY(appDataDir.mkpath(QStringLiteral("pics")));
0065         QVERIFY(QFile::copy(QStringLiteral(":/app-image.png"), appDataDir.filePath(QStringLiteral("pics/image1.png"))));
0066         QVERIFY(QFile::copy(QStringLiteral(":/app-image.png"), appDataDir.filePath(QStringLiteral("pics/image2.png"))));
0067 
0068         // set up a minimal Oxygen icon theme, in case it is not installed
0069         QVERIFY(testIconsDir.mkpath(QStringLiteral("oxygen/22x22/actions")));
0070         QVERIFY(testIconsDir.mkpath(QStringLiteral("oxygen/22x22/animations")));
0071         QVERIFY(testIconsDir.mkpath(QStringLiteral("oxygen/22x22/apps")));
0072         QVERIFY(testIconsDir.mkpath(QStringLiteral("oxygen/22x22/mimetypes")));
0073         QVERIFY(testIconsDir.mkpath(QStringLiteral("oxygen/32x32/apps")));
0074         QVERIFY(QFile::copy(QStringLiteral(":/oxygen.theme"), testIconsDir.filePath(QStringLiteral("oxygen/index.theme"))));
0075         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/apps/kde.png"))));
0076         QVERIFY(QFile::copy(QStringLiteral(":/anim-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/animations/process-working.png"))));
0077         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/text-plain.png"))));
0078         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/application-octet-stream.png"))));
0079         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/image-x-generic.png"))));
0080         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/video-x-generic.png"))));
0081         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/x-office-document.png"))));
0082         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/audio-x-generic.png"))));
0083         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/mimetypes/unknown.png"))));
0084         QVERIFY(QFile::copy(QStringLiteral(":/test-32x32.png"), testIconsDir.filePath(QStringLiteral("oxygen/32x32/apps/kde.png"))));
0085 
0086         // set up a minimal Breeze icon theme, fallback to oxygen
0087         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/actions")));
0088         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/animations")));
0089         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/apps")));
0090         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/mimetypes")));
0091         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/appsNoContext")));
0092         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/appsNoType")));
0093         QVERIFY(testIconsDir.mkpath(QStringLiteral("breeze/22x22/appsNoContextOrType")));
0094 
0095         const QString breezeThemeFile = testIconsDir.filePath(QStringLiteral("breeze/index.theme"));
0096         QVERIFY(QFile::copy(QStringLiteral(":/breeze.theme"), breezeThemeFile));
0097         // kde.png is missing, it should fallback to oxygen
0098         // QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/apps/kde.png"))));
0099         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"),
0100                             testIconsDir.filePath(QStringLiteral("breeze/22x22/appsNoContext/iconindirectorywithoutcontext.png"))));
0101         QVERIFY(
0102             QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/appsNoType/iconindirectorywithouttype.png"))));
0103         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"),
0104                             testIconsDir.filePath(QStringLiteral("breeze/22x22/appsNoContextOrType/iconindirectorywithoutcontextortype.png"))));
0105         QVERIFY(QFile::copy(QStringLiteral(":/anim-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/animations/process-working.png"))));
0106         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/text-plain.png"))));
0107         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/application-octet-stream.png"))));
0108         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/image-x-generic.png"))));
0109         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/video-x-generic.png"))));
0110         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/x-office-document.png"))));
0111         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/audio-x-generic.png"))));
0112         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/mimetypes/unknown.png"))));
0113         QVERIFY(QFile::copy(QStringLiteral(":/coloredsvgicon.svg"), testIconsDir.filePath(QStringLiteral("breeze/22x22/apps/coloredsvgicon.svg"))));
0114 
0115         // prepare some icons for our actions test
0116         // when querying breeze for 'one-two', we expect
0117         // 'one' from breeze instead of oxygen's 'one-two'.
0118         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("oxygen/22x22/actions/one-two.png"))));
0119         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), testIconsDir.filePath(QStringLiteral("breeze/22x22/actions/one.png"))));
0120 
0121         QVERIFY(QFile::setPermissions(breezeThemeFile, QFileDevice::ReadOwner | QFileDevice::WriteOwner));
0122         KConfig configFile(breezeThemeFile);
0123         KConfigGroup iconThemeGroup = configFile.group("Icon Theme");
0124         QVERIFY(iconThemeGroup.isValid());
0125         QStringList dirs = iconThemeGroup.readEntry("Directories", QStringList());
0126         for (int i : testSizes) {
0127             const QString relDir = QStringLiteral("%1x%1/emblems").arg(i);
0128             const QString dir = testIconsDir.filePath(QStringLiteral("breeze/") + relDir);
0129             QVERIFY(QDir().mkpath(dir));
0130 
0131             QPixmap img(i, i);
0132             img.fill(Qt::red);
0133             QVERIFY(img.save(dir + QStringLiteral("/red.png")));
0134 
0135             dirs += relDir;
0136             KConfigGroup dirGroup = configFile.group(relDir);
0137             dirGroup.writeEntry("Size", i);
0138             dirGroup.writeEntry("Context", "Emblems");
0139             dirGroup.writeEntry("Type", "Fixed");
0140         }
0141         iconThemeGroup.writeEntry("Directories", dirs);
0142         QVERIFY(configFile.sync());
0143     }
0144 
0145     void cleanupTestCase()
0146     {
0147         if (testIconsDir != QDir()) {
0148             testIconsDir.removeRecursively();
0149         }
0150         if (appDataDir != QDir()) {
0151             appDataDir.removeRecursively();
0152         }
0153     }
0154 
0155     void init()
0156     {
0157         // Remove icon cache
0158         const QString cacheFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/icon-cache.kcache");
0159         QFile::remove(cacheFile);
0160 
0161         // Clear SHM cache
0162         KIconLoader::global()->reconfigure(QString());
0163     }
0164 
0165     void testUnknownIconNotCached()
0166     {
0167         // This is a test to ensure that "unknown" icons do not pin themselves
0168         // in the icon loader. Or in other words, if an "unknown" icon is
0169         // returned, but the appropriate icon is subsequently installed
0170         // properly, the next request for that icon should return the new icon
0171         // instead of the unknown icon.
0172 
0173         QString actionIconsSubdir = QStringLiteral("oxygen/22x22/actions");
0174         QVERIFY(testIconsDir.mkpath(actionIconsSubdir));
0175         QString actionIconsDir = testIconsDir.filePath(actionIconsSubdir);
0176 
0177         QString nonExistingIconName = QStringLiteral("fhqwhgads_homsar");
0178         QString newIconPath = actionIconsDir + QLatin1String("/") + nonExistingIconName + QLatin1String(".png");
0179         QFile::remove(newIconPath);
0180 
0181         KIconLoader iconLoader;
0182 
0183         // Find a non-existent icon, allowing unknown icon to be returned
0184         QPixmap nonExistingIcon = iconLoader.loadIcon(nonExistingIconName, KIconLoader::Toolbar);
0185         QCOMPARE(nonExistingIcon.isNull(), false);
0186 
0187         // Install the existing icon by copying.
0188         QVERIFY(QFile::copy(QStringLiteral(":/test-22x22.png"), newIconPath));
0189 
0190         // Verify the icon can now be found.
0191         QPixmap nowExistingIcon = iconLoader.loadIcon(nonExistingIconName, KIconLoader::Toolbar);
0192         QVERIFY(nowExistingIcon.cacheKey() != nonExistingIcon.cacheKey());
0193         QCOMPARE(iconLoader.iconPath(nonExistingIconName, KIconLoader::Toolbar), newIconPath);
0194     }
0195 
0196     void testLoadIconCanReturnNull()
0197     {
0198         // This is a test for the "canReturnNull" argument of KIconLoader::loadIcon().
0199         // We try to load an icon that doesn't exist, first with canReturnNull=false (the default)
0200         // then with canReturnNull=true.
0201         KIconLoader iconLoader;
0202         // We expect a warning here... This doesn't work though, due to the extended debug
0203         // QTest::ignoreMessage(QtWarningMsg, "KIconLoader::loadIcon: No such icon \"this-icon-does-not-exist\"");
0204         QPixmap pix = iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist"), KIconLoader::Desktop, 16);
0205         QVERIFY(!pix.isNull());
0206         QCOMPARE(pix.size(), QSize(16, 16));
0207         // Try it again, to see if the cache interfers
0208         pix = iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist"), KIconLoader::Desktop, 16);
0209         QVERIFY(!pix.isNull());
0210         QCOMPARE(pix.size(), QSize(16, 16));
0211         // And now set canReturnNull to true
0212         pix =
0213             iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist"), KIconLoader::Desktop, 16, KIconLoader::DefaultState, QStringList(), nullptr, true);
0214         QVERIFY(pix.isNull());
0215         // Try getting the "unknown" icon again, to see if the above call didn't put a null icon into the cache...
0216         pix = iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist"), KIconLoader::Desktop, 16);
0217         QVERIFY(!pix.isNull());
0218         QCOMPARE(pix.size(), QSize(16, 16));
0219 
0220         // Another one, to clear "last" cache
0221         pix = iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist-either"), KIconLoader::Desktop, 16);
0222         QVERIFY(!pix.isNull());
0223         QCOMPARE(pix.size(), QSize(16, 16));
0224 
0225         // Now load the initial one again - do we get the warning again?
0226         pix = iconLoader.loadIcon(QStringLiteral("this-icon-does-not-exist"), KIconLoader::Desktop, 16);
0227         QVERIFY(!pix.isNull());
0228         QCOMPARE(pix.size(), QSize(16, 16));
0229 
0230         pix = iconLoader.loadIcon(QStringLiteral("#crazyname"), KIconLoader::NoGroup, 1600);
0231         QVERIFY(!pix.isNull());
0232         QCOMPARE(pix.size(), QSize(1600, 1600));
0233     }
0234 
0235     void testAppPicsDir()
0236     {
0237         KIconLoader appIconLoader(appName);
0238         QString iconPath = appIconLoader.iconPath(QStringLiteral("image1"), KIconLoader::User);
0239         QCOMPARE(iconPath, appDataDir.filePath(QStringLiteral("pics/image1.png")));
0240         QVERIFY(QFile::exists(iconPath));
0241 
0242         // Load it again, to use the "last loaded" cache
0243         QString iconPath2 = appIconLoader.iconPath(QStringLiteral("image1"), KIconLoader::User);
0244         QCOMPARE(iconPath, iconPath2);
0245         // Load something else, to clear the "last loaded" cache
0246         QString iconPathTextEdit = appIconLoader.iconPath(QStringLiteral("image2"), KIconLoader::User);
0247         QCOMPARE(iconPathTextEdit, appDataDir.filePath(QStringLiteral("pics/image2.png")));
0248         QVERIFY(QFile::exists(iconPathTextEdit));
0249         // Now load kdialog again, to use the real kiconcache
0250         iconPath2 = appIconLoader.iconPath(QStringLiteral("image1"), KIconLoader::User);
0251         QCOMPARE(iconPath, iconPath2);
0252     }
0253 
0254     void testAppPicsDir_KDE_icon()
0255     {
0256         // #### This test is broken; it passes even if appName is set to foobar, because
0257         // QIcon::pixmap returns an unknown icon if it can't find the real icon...
0258         KIconLoader appIconLoader(appName);
0259         // Now using KDE::icon. Separate test so that KIconLoader isn't fully inited.
0260         QIcon icon = KDE::icon(QStringLiteral("image1"), &appIconLoader);
0261         {
0262             QPixmap pix = icon.pixmap(QSize(22, 22));
0263             QVERIFY(!pix.isNull());
0264         }
0265         // Qt's implementation of actualSize does not crop to squares, ensure we don't do that either
0266         QCOMPARE(icon.actualSize(QSize(96, 22)), QSize(96, 22));
0267         QCOMPARE(icon.actualSize(QSize(22, 96)), QSize(22, 96));
0268         QCOMPARE(icon.actualSize(QSize(22, 16)), QSize(22, 16));
0269 
0270         // Can we ask for a really small size?
0271         {
0272             QPixmap pix8 = icon.pixmap(QSize(8, 8));
0273             QCOMPARE(pix8.size(), QSize(8, 8));
0274         }
0275     }
0276 
0277     void testLoadMimeTypeIcon_data()
0278     {
0279         QTest::addColumn<QString>("iconName");
0280         QTest::addColumn<QString>("expectedFileName");
0281 
0282         QTest::newRow("existing icon") << QStringLiteral("text-plain") << QStringLiteral("text-plain.png");
0283         QTest::newRow("octet-stream icon") << QStringLiteral("application-octet-stream") << QStringLiteral("application-octet-stream.png");
0284         QTest::newRow("non-existing icon") << QStringLiteral("foo-bar") << QStringLiteral("application-octet-stream.png");
0285         // Test this again, because now we won't go into the "fast path" of loadMimeTypeIcon anymore.
0286         QTest::newRow("existing icon again") << QStringLiteral("text-plain") << QStringLiteral("text-plain.png");
0287         QTest::newRow("generic fallback") << QStringLiteral("image-foo-bar") << QStringLiteral("image-x-generic.png");
0288         QTest::newRow("video generic fallback") << QStringLiteral("video-foo-bar") << QStringLiteral("video-x-generic.png");
0289         QTest::newRow("image-x-generic itself") << QStringLiteral("image-x-generic") << QStringLiteral("image-x-generic.png");
0290         QTest::newRow("x-office-document icon") << QStringLiteral("x-office-document") << QStringLiteral("x-office-document.png");
0291         QTest::newRow("unavailable generic icon") << QStringLiteral("application/x-font-vfont") << QStringLiteral("application-octet-stream.png");
0292         QTest::newRow("#184852") << QStringLiteral("audio/x-tuxguitar") << QStringLiteral("audio-x-generic.png");
0293         QTest::newRow("#178847") << QStringLiteral("image/x-compressed-xcf") << QStringLiteral("image-x-generic.png");
0294         QTest::newRow("mimetype generic icon") << QStringLiteral("application-x-fluid") << QStringLiteral("x-office-document.png");
0295     }
0296 
0297     void testLoadMimeTypeIcon()
0298     {
0299         QFETCH(QString, iconName);
0300         QFETCH(QString, expectedFileName);
0301         KIconLoader iconLoader;
0302         QString path;
0303         QPixmap pix = iconLoader.loadMimeTypeIcon(iconName, KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0304         QVERIFY(!pix.isNull());
0305         QCOMPARE(path.section(QLatin1Char('/'), -1), expectedFileName);
0306 
0307         // do the same test using a global iconloader, so that
0308         // we get into the final return statement, which can only happen
0309         // if d->extraDesktopIconsLoaded becomes true first....
0310         QString path2;
0311         pix = KIconLoader::global()->loadMimeTypeIcon(iconName, KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path2);
0312         QVERIFY(!pix.isNull());
0313         QCOMPARE(path2, path);
0314     }
0315 
0316     void testHasIcon()
0317     {
0318         // Do everything twice to check code paths that might use a cache
0319         QVERIFY(KIconLoader::global()->hasIcon(QStringLiteral("kde")));
0320         QVERIFY(KIconLoader::global()->hasIcon(QStringLiteral("kde")));
0321         KIconLoader iconLoader;
0322         QVERIFY(iconLoader.hasIcon(QStringLiteral("kde")));
0323 
0324         QVERIFY(KIconLoader::global()->hasIcon(QStringLiteral("process-working")));
0325         QVERIFY(KIconLoader::global()->hasIcon(QStringLiteral("process-working")));
0326         QVERIFY(!KIconLoader::global()->hasIcon(QStringLiteral("no-such-icon-exists")));
0327         QVERIFY(!KIconLoader::global()->hasIcon(QStringLiteral("no-such-icon-exists")));
0328     }
0329 
0330     void testIconPath()
0331     {
0332         // Test iconPath with non-existing icon
0333         const QString path = KIconLoader::global()->iconPath(QStringLiteral("nope-no-such-icon"), KIconLoader::Desktop, true /*canReturnNull*/);
0334         QVERIFY2(path.isEmpty(), qPrintable(path));
0335 
0336         const QString unknownPath = KIconLoader::global()->iconPath(QStringLiteral("nope-no-such-icon"), KIconLoader::Desktop, false);
0337         QVERIFY(!unknownPath.isEmpty());
0338         QVERIFY(QFile::exists(unknownPath));
0339     }
0340 
0341     void testCorrectFallback()
0342     {
0343         // we want to prefer icons from the same theme
0344 
0345         // so if we have something like:
0346         /*
0347             oxygen:
0348                 one-two
0349 
0350             breeze:
0351                 one
0352         */
0353         // and we ask for 'one-two', we expect to see 'one' from breeze instead
0354         // of 'one-two' from oxygen.
0355         QString path;
0356         KIconLoader::global()->loadIcon(QStringLiteral("one-two"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0357         QVERIFY(path.contains("breeze/22x22/actions"));
0358     }
0359 
0360     void testPathStore()
0361     {
0362         QString path;
0363         QPixmap pix = KIconLoader::global()->loadIcon(QStringLiteral("kde"), KIconLoader::Desktop, 0, KIconLoader::DefaultState, QStringList(), &path);
0364         QVERIFY(!path.isEmpty());
0365         QVERIFY(QFile::exists(path));
0366         QVERIFY2(path.contains(QLatin1String("32x32")), qPrintable(path));
0367         QCOMPARE(pix.size(), QSize(32, 32));
0368 
0369         // Compare with iconPath()
0370         QString path2 = KIconLoader::global()->iconPath(QStringLiteral("kde"), KIconLoader::Desktop);
0371         QCOMPARE(path2, path);
0372 
0373         // Now specify a size
0374         pix = KIconLoader::global()->loadIcon(QStringLiteral("kde"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0375         QVERIFY(!path.isEmpty());
0376         QVERIFY(QFile::exists(path));
0377         QVERIFY2(path.contains(QLatin1String("22x22")), qPrintable(path));
0378         QCOMPARE(pix.size(), QSize(24, 24));
0379 
0380         QVERIFY(KIconLoader::global()->hasIcon(QStringLiteral("kde")));
0381 
0382         path = QString();
0383         KIconLoader::global()
0384             ->loadIcon(QStringLiteral("does_not_exist"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path, true /* canReturnNull */);
0385         QVERIFY2(path.isEmpty(), qPrintable(path));
0386 
0387         path = QStringLiteral("some filler to check loadIcon() clears the variable");
0388         KIconLoader::global()
0389             ->loadIcon(QStringLiteral("does_not_exist"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path, true /* canReturnNull */);
0390         QVERIFY2(path.isEmpty(), qPrintable(path));
0391 
0392         // Test that addAppDir doesn't break loading of icons from the old known paths
0393         KIconLoader loader;
0394         // only addAppDir
0395         loader.addAppDir(QStringLiteral("kiconloader_unittest"));
0396         path = QString();
0397         loader.loadIcon(QStringLiteral("kde"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0398         QVERIFY(!path.isEmpty());
0399 
0400         path = QString();
0401         loader.loadIcon(QStringLiteral("image1"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0402         QVERIFY(!path.isEmpty());
0403 
0404         // only reconfigure
0405         KIconLoader loader2;
0406         path = QString();
0407         loader2.reconfigure(QStringLiteral("kiconloader_unittest"));
0408         loader2.loadIcon(QStringLiteral("kde"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0409         QVERIFY(!path.isEmpty());
0410         loader2.loadIcon(QStringLiteral("image1"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0411         QVERIFY(!path.isEmpty());
0412 
0413         // both addAppDir and reconfigure
0414         KIconLoader loader3;
0415         path = QString();
0416         loader3.addAppDir(QStringLiteral("kiconloader_unittest"));
0417         loader3.reconfigure(QStringLiteral("kiconloader_unittest"));
0418         loader3.loadIcon(QStringLiteral("kde"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0419         QVERIFY(!path.isEmpty());
0420 
0421         path = QString();
0422         loader3.loadIcon(QStringLiteral("image1"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0423         QVERIFY(!path.isEmpty());
0424     }
0425 
0426     void testPathsNoContextType()
0427     {
0428         {
0429             QString path;
0430             KIconLoader::global()
0431                 ->loadIcon(QStringLiteral("iconindirectorywithoutcontext"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0432             QVERIFY(path.endsWith(QLatin1String("appsNoContext/iconindirectorywithoutcontext.png")));
0433         }
0434         {
0435             QString path;
0436             KIconLoader::global()
0437                 ->loadIcon(QStringLiteral("iconindirectorywithouttype"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0438             QVERIFY(path.endsWith(QLatin1String("appsNoType/iconindirectorywithouttype.png")));
0439         }
0440         {
0441             QString path;
0442             KIconLoader::global()
0443                 ->loadIcon(QStringLiteral("iconindirectorywithoutcontextortype"), KIconLoader::Desktop, 24, KIconLoader::DefaultState, QStringList(), &path);
0444             QVERIFY(path.endsWith(QLatin1String("appsNoContextOrType/iconindirectorywithoutcontextortype.png")));
0445         }
0446     }
0447 
0448     void testLoadIconNoGroupOrSize() // #246016
0449     {
0450         QPixmap pix = KIconLoader::global()->loadIcon(QStringLiteral("connected"), KIconLoader::NoGroup);
0451         QVERIFY(!pix.isNull());
0452     }
0453 
0454     void testUnknownIcon()
0455     {
0456         QPixmap pix = KIconLoader::unknown();
0457         QPixmap pix2 = KIconLoader::unknown();
0458         QCOMPARE(pix.cacheKey(), pix2.cacheKey());
0459     }
0460 
0461     void testLoadPixmapSequence()
0462     {
0463         KPixmapSequence seq = KIconLoader::global()->loadPixmapSequence(QStringLiteral("process-working"), 22);
0464         QVERIFY(seq.isValid());
0465     }
0466 
0467     void testAppropriateSizes()
0468     {
0469         const KIconLoader iconLoader;
0470         const QRegularExpression rx(QStringLiteral("/(\\d+)x\\d+/"));
0471         for (int i = 1; i < testSizes.last() * 1.2; i += 3) {
0472             QString path;
0473             QPixmap pix = iconLoader.loadIcon(QStringLiteral("red"), KIconLoader::Desktop, i, KIconLoader::DefaultState, QStringList(), &path);
0474             QVERIFY(!path.isEmpty());
0475             QVERIFY(!pix.isNull());
0476 
0477             const QRegularExpressionMatch match = rx.match(path);
0478             QVERIFY(match.isValid());
0479 
0480             const int foundSize = match.captured(1).toInt();
0481             int ts = testSizes.last();
0482             for (int w = 0; w < testSizes.size(); w++) {
0483                 const int curr = testSizes[w];
0484                 if (curr >= i) {
0485                     ts = curr;
0486                     break;
0487                 }
0488             }
0489             QVERIFY(ts == foundSize);
0490         }
0491     }
0492 
0493     void testColoredSvgIcon()
0494     {
0495         QPalette pal = qApp->palette();
0496         pal.setColor(QPalette::WindowText, QColor(255, 0, 0));
0497         qApp->setPalette(pal);
0498         QImage img = KIconLoader::global()->loadIcon(QStringLiteral("coloredsvgicon"), KIconLoader::NoGroup).toImage();
0499         QVERIFY(!img.isNull());
0500         // Has the image been recolored to red,
0501         // that is the color we wrote in kdeglobals as text color?
0502         QCOMPARE(img.pixel(0, 0), (uint)4294901760);
0503     }
0504 
0505     void testUintToHex()
0506     {
0507         // HEX (ARGB format without the #): ff6496c8
0508         QColor testColorNoAlpha(100, 150, 200);
0509 
0510         // The ARGB string in which the composed hex value is stored.
0511         QString argbHex(8, Qt::Uninitialized);
0512 
0513         // Verify the ARGB hex (ff6496c8)
0514         uintToHex(testColorNoAlpha.rgba(), argbHex.data());
0515         QCOMPARE(argbHex, QStringLiteral("ff6496c8"));
0516 
0517         // HEX (ARGB format without the #): 7b6496c8
0518         QColor testColorWithAlpha(100, 150, 200, 123);
0519 
0520         // Test uintToHex to verify its ARGB output.
0521         uintToHex(testColorWithAlpha.rgba(), argbHex.data());
0522         QCOMPARE(argbHex, QStringLiteral("7b6496c8"));
0523     }
0524 
0525     void testQDirSetSearchPaths()
0526     {
0527         // setup search path for testprefix: => we shall find the iconinspecialsearchpath.svg afterwards, see e.g. bug 434451
0528         QDir::setSearchPaths("testprefix", QStringList(":/searchpathdefineddir"));
0529         QPixmap pix = KIconLoader::global()->loadIcon(QStringLiteral("testprefix:iconinspecialsearchpath.svg"),
0530                                                       KIconLoader::NoGroup,
0531                                                       24,
0532                                                       KIconLoader::DefaultState,
0533                                                       QStringList(),
0534                                                       nullptr,
0535                                                       true);
0536         QVERIFY(!pix.isNull());
0537     }
0538 };
0539 
0540 QTEST_MAIN(KIconLoader_UnitTest)
0541 
0542 #include "kiconloader_unittest.moc"