File indexing completed on 2024-10-13 06:35:34

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