File indexing completed on 2022-11-29 18:25:16

0001 /*
0002  *  Copyright (C) 2005-2009 David Faure   <faure@kde.org>
0003  *
0004  *  This library is free software; you can redistribute it and/or modify
0005  *  it under the terms of the GNU Lesser General Public License as published by
0006  *  the Free Software Foundation; either version 2 of the License or ( at
0007  *  your option ) version 3 or, at the discretion of KDE e.V. ( which shall
0008  *  act as a proxy as in section 14 of the GPLv3 ), any later version.
0009  *
0010  *  This library 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 GNU
0013  *  Library General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Library General Public License
0016  *  along with this library; see the file COPYING.LIB.  If not, write to
0017  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  *  Boston, MA 02110-1301, USA.
0019  */
0020 
0021 #include "kmimetypetest.h"
0022 
0023 #include <kde_file.h>
0024 #include <kdeversion.h> // KDE_MAKE_VERSION
0025 #include <kmimetype.h>
0026 #include <ksycoca.h>
0027 #include <kuser.h>
0028 #include <qtemporarydir.h>
0029 #include <kconfiggroup.h>
0030 #include <kdebug.h>
0031 
0032 #include <qtest_kde.h> // WARNING: do not port to qtest.h without adding a putenv for XDG_DATA_HOME! User data loss will occur otherwise.
0033 #include <qstandardpaths.h>
0034 #include <qprocess.h>
0035 #include <kmimetypetrader.h>
0036 #include <kservicetypetrader.h>
0037 #include <kmimetyperepository_p.h>
0038 #include <qtemporaryfile.h>
0039 #include <kdesktopfile.h>
0040 
0041 #include <QtConcurrent>
0042 
0043 extern KSERVICE_EXPORT bool kservice_require_kded;
0044 
0045 static int initializeLang()
0046 {
0047     qputenv("LC_ALL", "en_US");
0048     qputenv("LANG", "en_US");
0049     qputenv("XDG_CURRENT_DESKTOP", "KDE");
0050     kservice_require_kded = false;
0051     return 0;
0052 }
0053 
0054 // Set LANG before QCoreApplication is created
0055 Q_CONSTRUCTOR_FUNCTION(initializeLang)
0056 
0057 void KMimeTypeTest::initTestCase()
0058 {
0059     // Clean up local xdg dir in case of leftover mimetype definitions
0060     const QString xdgDir = QString::fromLocal8Bit(getenv("XDG_DATA_HOME"));
0061     if (!xdgDir.isEmpty()) {
0062         QDir d_(xdgDir);
0063         d_.removeRecursively();
0064         // No need to run update-mime-database here, the dir is entirely gone.
0065     }
0066 
0067     bool mustUpdateKSycoca = false;
0068 
0069     // Create fake text/x-patch part.
0070     const QString fakePatchPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepatchpart.desktop";
0071     const bool mustCreatePatchPart = !QFile::exists(fakePatchPart);
0072     if (mustCreatePatchPart) {
0073         mustUpdateKSycoca = true;
0074         KDesktopFile file(fakePatchPart);
0075         KConfigGroup group = file.desktopGroup();
0076         group.writeEntry("Name", "FakePatchPart");
0077         group.writeEntry("Type", "Service");
0078         group.writeEntry("X-KDE-Library", "fakepatchpart");
0079         group.writeEntry("ServiceTypes", "KParts/ReadOnlyPart");
0080         group.writeEntry("MimeType", "text/x-diff;"); // Use an alias on purpose, to test if that works
0081         group.writeEntry("InitialPreference", 5);
0082     }
0083 
0084     // Create fake text/plain part with a higher initial preference than the patch part.
0085     QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "faketextpart.desktop";
0086     const bool mustCreate = !QFile::exists(fakePart);
0087     if (mustCreate) {
0088         mustUpdateKSycoca = true;
0089         KDesktopFile file(fakePart);
0090         KConfigGroup group = file.desktopGroup();
0091         group.writeEntry("Name", "FakePart");
0092         group.writeEntry("Type", "Service");
0093         group.writeEntry("X-KDE-Library", "faketextpart");
0094         group.writeEntry("ServiceTypes", "KParts/ReadOnlyPart");
0095         group.writeEntry("MimeType", "text/plain;");
0096         group.writeEntry("InitialPreference", 100);
0097     }
0098 
0099     // Create fake text/plain ktexteditor plugin.
0100     QString fakePlugin = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "faketextplugin.desktop";
0101     const bool mustCreatePlugin = !QFile::exists(fakePlugin);
0102     if (mustCreatePlugin) {
0103         mustUpdateKSycoca = true;
0104         KDesktopFile file(fakePlugin);
0105         KConfigGroup group = file.desktopGroup();
0106         group.writeEntry("Name", "FakePlugin");
0107         group.writeEntry("Type", "Service");
0108         group.writeEntry("X-KDE-Library", "faketextplugin");
0109         group.writeEntry("ServiceTypes", "KPluginInfo");
0110         group.writeEntry("MimeType", "text/plain;");
0111     }
0112 
0113     // Create fake "NotShowIn=KDE" service
0114     m_nonKdeApp = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1Char('/') + "fake_nonkde_application.desktop";
0115     const bool mustCreateNonKdeApp = !QFile::exists(m_nonKdeApp);
0116     if (mustCreateNonKdeApp) {
0117         mustUpdateKSycoca = true;
0118         KDesktopFile file(m_nonKdeApp);
0119         KConfigGroup group = file.desktopGroup();
0120         group.writeEntry("Name", "NonKDEApp");
0121         group.writeEntry("Type", "Application");
0122         group.writeEntry("Exec", "xterm");
0123         group.writeEntry("NotShowIn", "KDE;FVWM;");
0124         group.writeEntry("MimeType", "text/plain;");
0125         group.writeEntry("InitialPreference", "50");
0126         group.writeEntry("Categories", "Qt;KDE;");
0127     }
0128     m_nonKdeApp = QFileInfo(m_nonKdeApp).canonicalFilePath();
0129 
0130     // Create fake text/plain app
0131     m_textPlainApp = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1Char('/') + "fake_textplain_application.desktop";
0132     const bool mustCreateTextPlainApp = !QFile::exists(m_textPlainApp);
0133     if (mustCreateTextPlainApp) {
0134         mustUpdateKSycoca = true;
0135         KDesktopFile file(m_textPlainApp);
0136         KConfigGroup group = file.desktopGroup();
0137         group.writeEntry("Name", "NonKDEApp");
0138         group.writeEntry("Type", "Application");
0139         group.writeEntry("Exec", "xterm");
0140         group.writeEntry("MimeType", "text/plain;");
0141         group.writeEntry("InitialPreference", "40");
0142         group.writeEntry("Categories", "Qt;KDE;");
0143     }
0144     m_textPlainApp = QFileInfo(m_textPlainApp).canonicalFilePath();
0145 
0146     if (mustUpdateKSycoca) {
0147         // Update ksycoca in ~/.kde-unit-test after creating the above
0148         QProcess::execute(QStandardPaths::findExecutable(KBUILDSYCOCA_EXENAME), QStringList());
0149     }
0150 
0151     QVERIFY(KService::serviceByStorageId("fake_nonkde_application.desktop"));
0152     QVERIFY(KService::serviceByDesktopPath(m_nonKdeApp)); // the desktoppath is the full path nowadays
0153     QVERIFY(KService::serviceByStorageId("fake_textplain_application.desktop"));
0154     QVERIFY(KService::serviceByDesktopPath(m_textPlainApp));
0155 }
0156 
0157 void KMimeTypeTest::cleanupTestCase()
0158 {
0159     // If I want the konqueror unit tests to work, then I better not have a non-working part
0160     // as the preferred part for text/plain...
0161     const QString fakePatchPart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "fakepatchpart.desktop";
0162     QFile::remove(fakePatchPart);
0163     const QString fakePart = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "faketextpart.desktop";
0164     QFile::remove(fakePart);
0165     const QString fakePlugin = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/kservices5/") + "faketextplugin.desktop";
0166     QFile::remove(fakePlugin);
0167     QFile::remove(m_textPlainApp);
0168     QFile::remove(m_nonKdeApp);
0169     QProcess proc;
0170     proc.setProcessChannelMode(QProcess::MergedChannels); // silence kbuildsycoca output
0171     proc.start(QStandardPaths::findExecutable(KBUILDSYCOCA_EXENAME), QStringList());
0172     proc.waitForFinished();
0173 }
0174 
0175 QTEST_KDEMAIN_CORE(KMimeTypeTest)
0176 
0177 void KMimeTypeTest::testByName()
0178 {
0179     KMimeType::Ptr s0 = KMimeType::mimeType("application/x-zerosize");
0180     QVERIFY(s0);
0181     QCOMPARE(s0->name(), QString::fromLatin1("application/x-zerosize"));
0182     QCOMPARE(s0->comment(), QString::fromLatin1("empty document"));
0183 
0184     KMimeType::Ptr s0Again = KMimeType::mimeType("application/x-zerosize");
0185     QCOMPARE(s0Again->name(), s0->name());
0186     QVERIFY(s0Again != s0);
0187 
0188     KMimeType::Ptr s1 = KMimeType::mimeType("text/plain");
0189     QVERIFY(s1);
0190     QCOMPARE(s1->name(), QString::fromLatin1("text/plain"));
0191     //qDebug("Comment is %s", qPrintable(s1->comment()) );
0192 
0193     KMimeType::Ptr krita = KMimeType::mimeType("application/x-krita");
0194     QVERIFY(krita);
0195 
0196     // Test <comment> parsing with application/rdf+xml which has the english comment after the other ones
0197     KMimeType::Ptr rdf = KMimeType::mimeType("application/rdf+xml");
0198     QVERIFY(rdf);
0199     QCOMPARE(rdf->comment(), QString::fromLatin1("RDF file"));
0200 
0201     KMimeType::Ptr bzip2 = KMimeType::mimeType("application/x-bzip2");
0202     QVERIFY(bzip2);
0203     QCOMPARE(bzip2->comment(), QString::fromLatin1("Bzip archive"));
0204 
0205     KMimeType::Ptr defaultMime = KMimeType::mimeType("application/octet-stream");
0206     QVERIFY(defaultMime);
0207     QVERIFY(defaultMime->isDefault());
0208 }
0209 
0210 void KMimeTypeTest::testIcons()
0211 {
0212     if (!KUser().isSuperUser()) { // Can't test this one if running as root
0213         QString emptyString; // gcc-3.3 workaround
0214         QTemporaryDir tmp(emptyString);
0215         QFile(tmp.path()).setPermissions(QFileDevice::Permissions());
0216         tmp.setAutoRemove(true);
0217         //KUrl url( tmp.path() );
0218         //QCOMPARE(KIO::iconNameForUrl(url), "inode-directory"); // was folder_locked, but we don't have that anymore - TODO
0219         QFile(tmp.path()).setPermissions(QFile::ReadOwner | QFile::ExeOwner); // so we can 'rm -rf' it
0220     }
0221 }
0222 
0223 void KMimeTypeTest::testFindByPathUsingFileName_data()
0224 {
0225     QTest::addColumn<QString>("fileName");
0226     QTest::addColumn<QString>("expectedMimeType");
0227     // Maybe we could also add a expectedAccuracy column...
0228 
0229     QTest::newRow("text") << "textfile.txt" << "text/plain";
0230     QTest::newRow("case-insensitive search") << "textfile.TxT" << "text/plain";
0231     // With QMime, this needs shared-mime-info > 0.91. Earlier versions wrote .Z to the mime.cache file...
0232     if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 91, 0)) {
0233         QTest::newRow("case-insensitive match on a non-lowercase glob") << "foo.z" << "application/x-compress";
0234     }
0235 
0236     QTest::newRow("case-sensitive uppercase match") << "textfile.C" << "text/x-c++src";
0237     QTest::newRow("case-sensitive lowercase match") << "textfile.c" << "text/x-csrc";
0238     QTest::newRow("case-sensitive long-extension match") << "foo.PS.gz" << "application/x-gzpostscript";
0239     QTest::newRow("case-sensitive-only match") << "core" << "application/x-core";
0240     QTest::newRow("case-sensitive-only match") << "Core" << "application/octet-stream"; // #198477
0241 
0242     QTest::newRow("desktop file") << "foo.desktop" << "application/x-desktop";
0243     QTest::newRow("old kdelnk file is x-desktop too") << "foo.kdelnk" << "application/x-desktop";
0244     QTest::newRow("double-extension file") << "foo.tar.bz2" << "application/x-bzip-compressed-tar";
0245     QTest::newRow("single-extension file") << "foo.bz2" << "application/x-bzip";
0246     QTest::newRow(".doc should assume msword") << "somefile.doc" << "application/msword"; // #204139
0247     QTest::newRow("glob that uses [] syntax, 1") << "Makefile" << "text/x-makefile";
0248     QTest::newRow("glob that uses [] syntax, 2") << "makefile" << "text/x-makefile";
0249     QTest::newRow("glob that ends with *, no extension") << "README" << "text/x-readme";
0250     QTest::newRow("glob that ends with *, extension") << "README.foo" << "text/x-readme";
0251     QTest::newRow("glob that ends with *, also matches *.txt. Higher weight wins.") << "README.txt" << "text/plain";
0252     QTest::newRow("glob that ends with *, also matches *.nfo. Higher weight wins.") << "README.nfo" << "text/x-nfo";
0253     // fdo bug 15436, needs shared-mime-info >= 0.40 (and this tests the globs2-parsing code).
0254     QTest::newRow("glob that ends with *, also matches *.pdf. *.pdf has higher weight") << "README.pdf" << "application/pdf";
0255     QTest::newRow("directory") << "/" << "inode/directory";
0256     QTest::newRow("doesn't exist, no extension") << "IDontExist" << "application/octet-stream";
0257     QTest::newRow("doesn't exist but has known extension") << "IDontExist.txt" << "text/plain";
0258     QTest::newRow("png image") << QFINDTESTDATA("image.png") << "image/png";
0259 
0260 #ifdef Q_OS_WIN
0261     // Windows: use cmake
0262     const QString exePath = QStandardPaths::findExecutable("cmake");
0263     QVERIFY2(!exePath.isEmpty(), "cmake not found. Isn't it in your $PATH?");
0264     const QString executableType = QString::fromLatin1("application/x-ms-dos-executable");
0265 #else
0266     // Linux: cmake can be found as x-sharedlib on CI for some reason, use ls like tst_qmimedatabase does
0267     const QString exePath = QStandardPaths::findExecutable("ls");
0268     QVERIFY2(!exePath.isEmpty(), "ls not found. Isn't it in your $PATH?");
0269     const QString executableType = QString::fromLatin1("application/x-executable");
0270 #endif
0271     QTest::newRow("executable") << exePath << executableType;
0272 }
0273 
0274 void KMimeTypeTest::testFindByPathUsingFileName()
0275 {
0276     QFETCH(QString, fileName);
0277     QFETCH(QString, expectedMimeType);
0278     KMimeType::Ptr mime = KMimeType::findByPath(fileName);
0279     QVERIFY(mime);
0280     if (expectedMimeType == QLatin1String("application/x-executable")) {
0281         // see bf87e1cfbd in qtbase
0282         QVERIFY(mime->name() == expectedMimeType || mime->name() == "application/x-sharedlib");
0283     } else {
0284         QCOMPARE(mime->name(), expectedMimeType);
0285     }
0286 }
0287 
0288 void KMimeTypeTest::testAdditionalGlobs_data()
0289 {
0290     // Other globs that are not in shared-mime-info but which users could define themselves.
0291     QTest::addColumn<QString>("filename");
0292     QTest::addColumn<QString>("pattern");
0293     QTest::addColumn<bool>("expected");
0294 
0295     QTest::newRow("one star, match") << "foo.txt" << "*.txt" << true;
0296     QTest::newRow("README*, match") << "README.foo" << "README*" << true;
0297     QTest::newRow("README.*, match") << "README.foo" << "README.*" << true;
0298     QTest::newRow("README.*, no match") << "README" << "README.*" << false;
0299     QTest::newRow("two stars, match") << "andre.ts.001" << "*.ts.0*" << true;
0300     QTest::newRow("two stars, no match") << "andre.ts" << "*.ts.0*" << false;
0301 }
0302 
0303 void KMimeTypeTest::testAdditionalGlobs()
0304 {
0305     QFETCH(QString, filename);
0306     QFETCH(QString, pattern);
0307     QFETCH(bool, expected);
0308 
0309     QCOMPARE(KMimeTypeRepository::matchFileName(filename, pattern), expected);
0310 }
0311 
0312 // All the simple tests for findByPath are in testFindByPathUsingFileName_data.
0313 // In here we do the tests that need some content in a temporary file.
0314 void KMimeTypeTest::testFindByPathWithContent()
0315 {
0316     KMimeType::Ptr mime;
0317 
0318     // Test a real PDF file.
0319     // If we find x-matlab because it starts with '%' then we are not ordering by priority.
0320     QTemporaryFile tempFile;
0321     QVERIFY(tempFile.open());
0322     QString tempFileName = tempFile.fileName();
0323     tempFile.write("%PDF-");
0324     tempFile.close();
0325     mime = KMimeType::findByPath(tempFileName);
0326     QVERIFY(mime);
0327     QCOMPARE(mime->name(), QString::fromLatin1("application/pdf"));
0328     // fast mode cannot find the mimetype
0329     mime = KMimeType::findByPath(tempFileName, 0, true);
0330     QVERIFY(mime);
0331     QCOMPARE(mime->name(), QString::fromLatin1("application/octet-stream"));
0332 
0333     // Test the case where the extension doesn't match the contents: extension wins
0334     {
0335         QTemporaryFile txtTempFile(QDir::tempPath() + QLatin1String("/kmimetypetest_XXXXXX.txt"));
0336         QVERIFY(txtTempFile.open());
0337         txtTempFile.write("%PDF-");
0338         QString txtTempFileName = txtTempFile.fileName();
0339         txtTempFile.close();
0340         mime = KMimeType::findByPath(txtTempFileName);
0341         QVERIFY(mime);
0342         QCOMPARE(mime->name(), QString::fromLatin1("text/plain"));
0343         // fast mode finds the same
0344         mime = KMimeType::findByPath(txtTempFileName, 0, true);
0345         QVERIFY(mime);
0346         QCOMPARE(mime->name(), QString::fromLatin1("text/plain"));
0347     }
0348 
0349     // Now the case where extension differs from contents, but contents has >80 magic rule
0350     // XDG spec used to say: contents wins. But we can't sniff all files...
0351     // XDG spec has now been amended, extensions always win.
0352     {
0353         QTemporaryFile txtTempFile(QDir::tempPath() + QLatin1String("/kmimetypetest_XXXXXX.txt"));
0354         QVERIFY(txtTempFile.open());
0355         txtTempFile.write("<smil");
0356         QString txtTempFileName = txtTempFile.fileName();
0357         txtTempFile.close();
0358         mime = KMimeType::findByPath(txtTempFileName);
0359         QVERIFY(mime);
0360         QCOMPARE(mime->name(), QString::fromLatin1("text/plain"));
0361     }
0362 }
0363 
0364 void KMimeTypeTest::testFindByUrl()
0365 {
0366     // Tests with local files are already done in testFindByPath,
0367     // here we test for remote urls only.
0368     KMimeType::Ptr mime;
0369     mime = KMimeType::findByUrl(KUrl("http://foo/bar.png"));
0370     QVERIFY(mime);
0371 
0372     QCOMPARE(mime->name(), QString::fromLatin1("application/octet-stream"));     // HTTP can't know before downloading
0373 
0374     mime = KMimeType::findByUrl(KUrl("http://foo/s0/"));
0375     QCOMPARE(mime->name(), QString::fromLatin1("application/octet-stream"));     // HTTP can't know before downloading
0376 
0377 #if 0 // no such logic in QMimeType, we get default mimetype, KRun will figure it out
0378     if (!KProtocolInfo::isKnownProtocol(KUrl("man:/"))) {
0379         QSKIP("man protocol not installed");
0380     }
0381 
0382     mime = KMimeType::findByUrl(KUrl("man:/ls"));
0383     QVERIFY(mime);
0384     QCOMPARE(mime->name(), QString::fromLatin1("text/html"));
0385 
0386     mime = KMimeType::findByUrl(KUrl("man:/ls/"));
0387     QVERIFY(mime);
0388     QCOMPARE(mime->name(), QString::fromLatin1("text/html"));
0389 #endif
0390 
0391     mime = KMimeType::findByUrl(KUrl("fish://host/test1")); // like fish does, to test for known extensions
0392     QVERIFY(mime);
0393     QCOMPARE(mime->name(), QString::fromLatin1("application/octet-stream"));
0394 }
0395 
0396 void KMimeTypeTest::testFindByNameAndContent()
0397 {
0398     KMimeType::Ptr mime;
0399 
0400     QByteArray textData = "Hello world";
0401     // textfile -> text/plain. No extension -> mimetype is found from the contents.
0402     mime = KMimeType::findByNameAndContent("textfile", textData);
0403     QVERIFY(mime);
0404     QCOMPARE(mime->name(), QString::fromLatin1("text/plain"));
0405 
0406     // textfile.foo -> text/plain. Unknown extension -> mimetype is found from the contents.
0407     mime = KMimeType::findByNameAndContent("textfile.foo", textData);
0408     QVERIFY(mime);
0409     QCOMPARE(mime->name(), QString::fromLatin1("text/plain"));
0410 
0411     // textfile.doc -> application/msword. Text files called *.doc are very rare these days, and no longer defined in kde5.xml
0412     mime = KMimeType::findByNameAndContent("textfile.doc", textData);
0413     QVERIFY(mime);
0414     QCOMPARE(mime->name(), QString::fromLatin1("application/msword"));
0415 
0416     // mswordfile.doc -> application/msword. Found by contents, because of the above case.
0417     // Note that it's application/msword, not application/vnd.ms-word, since it's the former that is registered to IANA.
0418     QByteArray mswordData = "\320\317\021\340\241\261\032\341";
0419     mime = KMimeType::findByNameAndContent("mswordfile.doc", mswordData);
0420     QVERIFY(mime);
0421     if (mime->name() == "application/vnd.ms-word") { // this comes from /usr/share/mime/packages/libreoffice.xml....
0422         QEXPECT_FAIL("", "libreoffice.xml is messing with us", Continue);
0423     }
0424     // If you get powerpoint instead, then you're hit by https://bugs.freedesktop.org/show_bug.cgi?id=435 - upgrade to shared-mime-info >= 0.22
0425     QCOMPARE(mime->name(), QString::fromLatin1("application/msword"));
0426 
0427     // excelfile.xls -> application/vnd.ms-excel. Found by extension.
0428     mime = KMimeType::findByNameAndContent("excelfile.xls", mswordData /*same magic*/);
0429     QVERIFY(mime);
0430     QCOMPARE(mime->name(), QString::fromLatin1("application/vnd.ms-excel"));
0431 
0432     // textfile.xls -> application/vnd.ms-excel. Found by extension. User shouldn't rename a text file to .xls ;)
0433     mime = KMimeType::findByNameAndContent("textfile.xls", textData);
0434     QVERIFY(mime);
0435     QCOMPARE(mime->name(), QString::fromLatin1("application/vnd.ms-excel"));
0436 
0437 #if 0   // needs shared-mime-info >= 0.20
0438     QByteArray tnefData = "\x78\x9f\x3e\x22";
0439     mime = KMimeType::findByNameAndContent("tneffile", mswordData);
0440     QVERIFY(mime);
0441     QCOMPARE(mime->name(), QString::fromLatin1("application/vnd.ms-tnef"));
0442 #endif
0443 
0444     QByteArray pdfData = "%PDF-";
0445     mime = KMimeType::findByNameAndContent("foo", pdfData);
0446     QVERIFY(mime);
0447     QCOMPARE(mime->name(), QString::fromLatin1("application/pdf"));
0448 
0449     // High-priority rule (80)
0450     QByteArray phpData = "<?php";
0451     mime = KMimeType::findByNameAndContent("foo", phpData);
0452     QVERIFY(mime);
0453     QCOMPARE(mime->name(), QString::fromLatin1("application/x-php"));
0454 }
0455 
0456 void KMimeTypeTest::testFindByContent_data()
0457 {
0458     QTest::addColumn<QByteArray>("data");
0459     QTest::addColumn<QString>("expectedMimeType");
0460     QTest::newRow("simple text") << QByteArray("Hello world") << "text/plain";
0461     QTest::newRow("html: <html>") << QByteArray("<html>foo</html>") << "text/html";
0462 
0463     // fixed in smi-0.30, xml magic has prio 40
0464     QTest::newRow("html: comment+<html>") << QByteArray("<!--foo--><html>foo</html>") << "text/html";
0465     // https://bugs.freedesktop.org/show_bug.cgi?id=11259, fixed in smi-0.22
0466     QTest::newRow("html: <script>") << QByteArray("<script>foo</script>") << "text/html";
0467 
0468     QTest::newRow("pdf") << QByteArray("%PDF-") << "application/pdf";
0469     QTest::newRow("no mimetype known") << QByteArray("\261\032\341\265") << "application/octet-stream";
0470 
0471     QByteArray mswordData = "\320\317\021\340\241\261\032\341";
0472     // same as \xD0\xCF\x11\xE0 \xA1\xB1\x1A\xE1
0473     QVERIFY(KMimeType::isBufferBinaryData(mswordData));
0474     // We have no magic specific to msword data, so finding x-ole-storage is correct.
0475     // If you get powerpoint instead, then you're hit by https://bugs.freedesktop.org/show_bug.cgi?id=435 - upgrade to shared-mime-info >= 0.22
0476     QTest::newRow("msword") << mswordData << "application/x-ole-storage";
0477 }
0478 
0479 void KMimeTypeTest::testFindByContent()
0480 {
0481     QFETCH(QByteArray, data);
0482     QFETCH(QString, expectedMimeType);
0483 
0484     KMimeType::Ptr mime = KMimeType::findByContent(data);
0485     QVERIFY(mime);
0486     QCOMPARE(mime->name(), expectedMimeType);
0487 }
0488 
0489 void KMimeTypeTest::testFindByFileContent()
0490 {
0491     KMimeType::Ptr mime;
0492     int accuracy = 0;
0493 
0494     // Calling findByFileContent on a directory
0495     mime = KMimeType::findByFileContent("/", &accuracy);
0496     QVERIFY(mime);
0497     QCOMPARE(mime->name(), QString::fromLatin1("inode/directory"));
0498     QCOMPARE(accuracy, 100);
0499 
0500     // Albert calls findByFileContent with a URL instead of a path and gets 11021 as accuracy :)
0501     // It was not set inside findByFileContent -> fixed.
0502     mime = KMimeType::findByFileContent("file:///etc/passwd" /*bad example code, use a path instead*/, &accuracy);
0503     QVERIFY(mime);
0504     QCOMPARE(mime->name(), QString::fromLatin1("application/octet-stream"));
0505     QCOMPARE(accuracy, 0);
0506 }
0507 
0508 void KMimeTypeTest::testAllMimeTypes()
0509 {
0510     const KMimeType::List lst = KMimeType::allMimeTypes(); // does NOT include aliases
0511     QVERIFY(!lst.isEmpty());
0512 
0513     for (KMimeType::List::ConstIterator it = lst.begin();
0514             it != lst.end(); ++it) {
0515         const KMimeType::Ptr mime = (*it);
0516         const QString name = mime->name();
0517         //qDebug( "%s", qPrintable( name ) );
0518         QVERIFY(!name.isEmpty());
0519         QCOMPARE(name.count('/'), 1);
0520 
0521         const KMimeType::Ptr lookedupMime = KMimeType::mimeType(name);
0522         QVERIFY(lookedupMime);   // not null
0523 #if 0 // this just breaks too often to run on systems with uncontrolled files in /usr/share/mime/packages
0524         if (name != "application/vnd.ms-word" && name != "application/x-pkcs7-certificates"
0525                 && name != "application/x-x509-ca-cert"
0526                 && name != "application/x-vnd.kde.kexi" // due to /usr/share/mime/packages/kde.xml from KDE4
0527                 && name != "application/x-kexiproject-sqlite" // due to /usr/share/mime/packages/kde.xml from KDE4
0528                 && name != "application/vnd.sun.xml.base" // libreoffice.xml messing things up yet again
0529            ) {
0530             QCOMPARE(lookedupMime->name(), name);
0531             // if this fails, you have an alias defined as a real mimetype too!
0532             //
0533             // Note: this also happens with x-win-lnk when your kde.xml defines it as an alias, while
0534             // /usr/share/mime/packages/kde.xml defines it as a real mimetype. This is a false positive,
0535             // remove one of the kde.xml files.
0536             //
0537             // It also happens with application/x-pkcs7-certificates due to
0538             // /usr/share/mime/packages/gcr-crypto-types.xml. Remove that file and run
0539             // `update-mime-database /usr/share/mime`.
0540         }
0541 #endif
0542     }
0543 }
0544 
0545 void KMimeTypeTest::testAlias()
0546 {
0547     const KMimeType::Ptr canonical = KMimeType::mimeType("application/xml");
0548     QVERIFY(canonical);
0549     KMimeType::Ptr alias = KMimeType::mimeType("text/xml");
0550     QVERIFY(alias);
0551     QCOMPARE(alias->name(), QString("application/xml"));
0552 
0553     QVERIFY(alias->is("application/xml"));
0554     QVERIFY(canonical->is("text/xml"));
0555 
0556     // Test for bug 197346: does nspluginscan see that audio/mp3 already exists?
0557     bool mustWriteMimeType = !KMimeType::mimeType("audio/mp3");
0558     QVERIFY(!mustWriteMimeType);
0559 }
0560 
0561 void KMimeTypeTest::testMimeTypeParent()
0562 {
0563     // All file-like mimetypes inherit from octet-stream
0564     const KMimeType::Ptr wordperfect = KMimeType::mimeType("application/vnd.wordperfect");
0565     QVERIFY(wordperfect);
0566     QCOMPARE(wordperfect->parentMimeTypes().join(","), QString("application/octet-stream"));
0567     QVERIFY(wordperfect->is("application/octet-stream"));
0568 
0569     QVERIFY(KMimeType::mimeType("image/svg+xml-compressed")->is("application/x-gzip"));
0570 
0571     // Check that msword derives from ole-storage [it didn't in 0.20, but we added it to kde.xml]
0572     const KMimeType::Ptr msword = KMimeType::mimeType("application/msword");
0573     QVERIFY(msword);
0574     const KMimeType::Ptr olestorage = KMimeType::mimeType("application/x-ole-storage");
0575     QVERIFY(olestorage);
0576     QVERIFY(msword->is(olestorage->name()));
0577     QVERIFY(msword->is("application/octet-stream"));
0578 
0579     const KMimeType::Ptr directory = KMimeType::mimeType("inode/directory");
0580     QVERIFY(directory);
0581     QCOMPARE(directory->parentMimeTypes().count(), 0);
0582     QVERIFY(!directory->is("application/octet-stream"));
0583 
0584     // Check that text/x-patch knows that it inherits from text/plain (it says so explicitly)
0585     const KMimeType::Ptr plain = KMimeType::mimeType("text/plain");
0586     const KMimeType::Ptr derived = KMimeType::mimeType("text/x-patch");
0587     QVERIFY(derived);
0588     QCOMPARE(derived->parentMimeTypes().join(","), plain->name());
0589     QVERIFY(derived->is("text/plain"));
0590     QVERIFY(derived->is("application/octet-stream"));
0591 
0592     // Check that application/x-shellscript inherits from application/x-executable
0593     // (Otherwise KRun cannot start shellscripts...)
0594     // This is a test for multiple inheritance...
0595     const KMimeType::Ptr shellscript = KMimeType::mimeType("application/x-shellscript");
0596     QVERIFY(shellscript);
0597     QVERIFY(shellscript->is("text/plain"));
0598     QVERIFY(shellscript->is("application/x-executable"));
0599     const QStringList shellParents = shellscript->parentMimeTypes();
0600     QVERIFY(shellParents.contains("text/plain"));
0601     QVERIFY(shellParents.contains("application/x-executable"));
0602     QCOMPARE(shellParents.count(), 2); // only the above two
0603     const QStringList allShellParents = shellscript->allParentMimeTypes();
0604     QVERIFY(allShellParents.contains("text/plain"));
0605     QVERIFY(allShellParents.contains("application/x-executable"));
0606     QVERIFY(allShellParents.contains("application/octet-stream"));
0607     // Must be least-specific last, i.e. breadth first.
0608     QCOMPARE(allShellParents.last(), QString("application/octet-stream"));
0609 
0610     // Check that text/x-mrml knows that it inherits from text/plain (implicitly)
0611     const KMimeType::Ptr mrml = KMimeType::mimeType("text/x-mrml");
0612     if (!mrml) {
0613         QSKIP("kdelibs not installed");
0614     }
0615     QVERIFY(mrml->is("text/plain"));
0616     QVERIFY(mrml->is("application/octet-stream"));
0617 }
0618 
0619 void KMimeTypeTest::testMimeTypeInheritancePerformance()
0620 {
0621     // Check performance of is(). In kde3 the list of mimetypes with previews had 63 items...
0622     // We could get it with KServiceTypeTrader::self()->query("ThumbCreator") and the "MimeTypes"
0623     // property, but this would give variable results and requires other modules installed.
0624     QStringList mimeTypes; mimeTypes << "image/jpeg" << "image/png" << "image/tiff" << "text/plain" << "text/html";
0625     mimeTypes += mimeTypes;
0626     mimeTypes += mimeTypes;
0627     mimeTypes += mimeTypes;
0628     QCOMPARE(mimeTypes.count(), 40);
0629     KMimeType::Ptr mime = KMimeType::mimeType("text/x-chdr");
0630     QVERIFY(mime);
0631     QTime dt; dt.start();
0632     QBENCHMARK {
0633         QString match;
0634         foreach (const QString &mt, mimeTypes)
0635         {
0636             if (mime->is(mt)) {
0637                 match = mt;
0638                 // of course there would normally be a "break" here, but we're testing worse-case
0639                 // performance here
0640             }
0641         }
0642         QCOMPARE(match, QString("text/plain"));
0643     }
0644     // Results on David's machine (April 2009):
0645     // With the KMimeType::is() code that loaded every parent KMimeType:
0646     // 3.5 msec / 7,000,000 ticks / 5,021,498 instr. loads per iteration
0647     // After the QHash for parent mimetypes in ksycoca, removing the need to load full mimetypes:
0648     // 0.57 msec / 1,115,000 ticks / 938,356 instr. loads per iteration
0649     // After converting the QMap for aliases into a QHash too:
0650     // 0.48 msec / 960,000 ticks / 791,404 instr. loads per iteration
0651     // July 2010: After moving KMimeType out of ksycoca:
0652     // 0.21 msec / 494,000 ticks / 568,345 instr. loads per iteration
0653 }
0654 
0655 // Helper method for all the trader tests
0656 static bool offerListHasService(const KService::List &offers,
0657                                 const QString &entryPath)
0658 {
0659     bool found = false;
0660     KService::List::const_iterator it = offers.begin();
0661     for (; it != offers.end(); ++it) {
0662         if ((*it)->entryPath() == entryPath) {
0663             if (found) {  // should be there only once
0664                 qWarning("ERROR: %s was found twice in the list", qPrintable(entryPath));
0665                 return false; // make test fail
0666             }
0667             found = true;
0668         }
0669     }
0670     return found;
0671 }
0672 
0673 void KMimeTypeTest::testMimeTypeTraderForTextPlain()
0674 {
0675     if (!KSycoca::isAvailable()) {
0676         QSKIP("ksycoca not available");
0677     }
0678 
0679     // Querying mimetype trader for services associated with text/plain
0680     KService::List offers = KMimeTypeTrader::self()->query("text/plain", "KParts/ReadOnlyPart");
0681     QVERIFY(!offerListHasService(offers, "fakepatchpart.desktop"));
0682     QVERIFY(offerListHasService(offers, "faketextpart.desktop"));
0683 
0684     offers = KMimeTypeTrader::self()->query("text/plain", "KPluginInfo");
0685     QVERIFY(offers.count() > 0);
0686 
0687     // We should have at least the fake text plugin that we created for this.
0688     // (The actual plugins from kdelibs don't mention text/plain anymore)
0689     QVERIFY(offerListHasService(offers, "faketextplugin.desktop"));
0690 
0691     // We shouldn't have non-plugins
0692     QVERIFY(!offerListHasService(offers, "fakepatchpart.desktop"));
0693     QVERIFY(!offerListHasService(offers, "faketextpart.desktop"));
0694 }
0695 
0696 void KMimeTypeTest::testMimeTypeTraderForDerivedMimeType()
0697 {
0698     if (!KSycoca::isAvailable()) {
0699         QSKIP("ksycoca not available");
0700     }
0701 
0702     // Querying mimetype trader for services associated with text/x-patch, which inherits from text/plain
0703     KService::List offers = KMimeTypeTrader::self()->query("text/x-patch", "KParts/ReadOnlyPart");
0704     QVERIFY(offerListHasService(offers, "fakepatchpart.desktop"));
0705     QVERIFY(offerListHasService(offers, "faketextpart.desktop"));
0706     QVERIFY((*offers.begin())->entryPath() != "faketextpart.desktop");   // in the list, but not preferred
0707 
0708     offers = KMimeTypeTrader::self()->query("text/x-patch", "KPluginInfo");
0709     QVERIFY(offers.count() > 0);
0710 
0711     // We should have at least the fake text plugin that we created for this.
0712     // (The actual plugins from kdelibs don't mention text/plain anymore)
0713     QVERIFY(offerListHasService(offers, "faketextplugin.desktop"));
0714 
0715     offers = KMimeTypeTrader::self()->query("text/x-patch", "Application");
0716     QVERIFY(!offerListHasService(offers, "faketextpart.desktop"));
0717 
0718     // We shouldn't have non-kde apps
0719     Q_FOREACH (KService::Ptr service, offers) {
0720         kDebug() << service->name() << service->entryPath();
0721     }
0722 
0723     QVERIFY(!offerListHasService(offers, m_nonKdeApp));
0724 }
0725 
0726 void KMimeTypeTest::testPreferredService()
0727 {
0728     // The "NotShowIn=KDE" service should not be the preferred one!
0729     KService::Ptr serv = KMimeTypeTrader::self()->preferredService("text/plain");
0730     QVERIFY(serv);
0731     qDebug() << serv->entryPath();
0732     QVERIFY(serv->entryPath() != m_nonKdeApp);
0733     QCOMPARE(serv->entryPath(), m_textPlainApp);
0734 }
0735 
0736 void KMimeTypeTest::testMimeTypeTraderForAlias()
0737 {
0738     if (!KSycoca::isAvailable()) {
0739         QSKIP("ksycoca not available");
0740     }
0741 
0742     const KService::List referenceOffers = KMimeTypeTrader::self()->query("application/xml", "KParts/ReadOnlyPart");
0743     QVERIFY(offerListHasService(referenceOffers, "faketextpart.desktop"));
0744     QVERIFY(!offerListHasService(referenceOffers, "fakepatchpart.desktop"));
0745 
0746     // Querying mimetype trader for services associated with text/xml, which is an alias for application/xml
0747     const KService::List offers = KMimeTypeTrader::self()->query("text/xml", "KParts/ReadOnlyPart");
0748     QVERIFY(offerListHasService(offers, "faketextpart.desktop"));
0749     QVERIFY(!offerListHasService(offers, "fakepatchpart.desktop"));
0750 
0751     QCOMPARE(offers.count(), referenceOffers.count());
0752 }
0753 
0754 void KMimeTypeTest::testHasServiceType1() // with services constructed with a full path (rare)
0755 {
0756     QString faketextpartPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("/kservices5/") + "faketextpart.desktop");
0757     QVERIFY(!faketextpartPath.isEmpty());
0758     KService faketextpart(faketextpartPath);
0759     QVERIFY(faketextpart.hasMimeType("text/plain"));
0760     QVERIFY(!faketextpart.hasMimeType("text/x-patch")); // inherited mimetype; fails
0761     QVERIFY(!faketextpart.hasMimeType("image/png"));
0762     QVERIFY(faketextpart.hasServiceType("KParts/ReadOnlyPart"));
0763     QVERIFY(!faketextpart.hasServiceType("KParts/ReadWritePart"));
0764     QVERIFY(!faketextpart.hasServiceType("KPluginInfo"));
0765 
0766     QString textPluginPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1String("/kservices5/") + "faketextplugin.desktop");
0767     QVERIFY(!textPluginPath.isEmpty());
0768     KService textPlugin(textPluginPath);
0769     QVERIFY(textPlugin.hasServiceType("KPluginInfo"));
0770     QVERIFY(!textPlugin.hasServiceType("KParts/ReadOnlyPart"));
0771 }
0772 
0773 void KMimeTypeTest::testHasServiceType2() // with services coming from ksycoca
0774 {
0775     KService::Ptr faketextpart = KService::serviceByDesktopPath("faketextpart.desktop");
0776     QVERIFY(faketextpart);
0777     QVERIFY(faketextpart->hasMimeType("text/plain"));
0778     QVERIFY(faketextpart->hasMimeType("text/x-patch"));     // due to inheritance
0779     QVERIFY(!faketextpart->hasMimeType("image/png"));
0780     QVERIFY(faketextpart->hasServiceType("KParts/ReadOnlyPart"));
0781     QVERIFY(!faketextpart->hasServiceType("KParts/ReadWritePart"));
0782     QVERIFY(!faketextpart->hasServiceType("KPluginInfo"));
0783 
0784     KService::Ptr textPlugin = KService::serviceByDesktopPath("faketextplugin.desktop");
0785     QVERIFY(textPlugin);
0786     QVERIFY(textPlugin->hasServiceType("KPluginInfo"));
0787     QVERIFY(!textPlugin->hasServiceType("KParts/ReadOnlyPart"));
0788 }
0789 
0790 void KMimeTypeTest::testPatterns_data()
0791 {
0792     QTest::addColumn<QString>("mimeType");
0793     QTest::addColumn<QString>("patterns");
0794     QTest::addColumn<QString>("mainExtension");
0795     QTest::newRow("mimetype with a single pattern") << "application/pdf" << "*.pdf" << ".pdf";
0796     QTest::newRow("mimetype with multiple patterns") << "application/x-kpresenter" << "*.kpr;*.kpt" << ".kpr";
0797     if (KMimeType::sharedMimeInfoVersion() > KDE_MAKE_VERSION(0, 60, 0)) {
0798         QTest::newRow("mimetype with many patterns") << "application/vnd.wordperfect" << "*.wp;*.wp4;*.wp5;*.wp6;*.wpd;*.wpp" << ".wp";
0799     }
0800     QTest::newRow("oasis text mimetype") << "application/vnd.oasis.opendocument.text" << "*.odt" << ".odt";
0801     QTest::newRow("oasis presentation mimetype") << "application/vnd.oasis.opendocument.presentation" << "*.odp" << ".odp";
0802     QTest::newRow("mimetype with multiple patterns") << "text/plain" << "*.asc;*.txt;*,v" << ".txt";
0803     QTest::newRow("mimetype with uncommon pattern") << "application/x-kcachegrind" << "callgrind.out*;cachegrind.out*" << QString();
0804     QTest::newRow("mimetype with no patterns") << "application/x-ole-storage" << QString() << QString();
0805 }
0806 
0807 void KMimeTypeTest::testPatterns()
0808 {
0809     QFETCH(QString, mimeType);
0810     QFETCH(QString, patterns);
0811     QFETCH(QString, mainExtension);
0812     KMimeType::Ptr mime = KMimeType::mimeType(mimeType);
0813     QVERIFY(mime);
0814     // Sort both lists; order is unreliable since shared-mime-info uses hashes internally.
0815     QStringList expectedPatterns = patterns.split(';', QString::SkipEmptyParts);
0816     expectedPatterns.sort();
0817     QStringList mimePatterns = mime->patterns();
0818 
0819     if (mimeType == "application/vnd.oasis.opendocument.text" && mimePatterns.contains("*.fodt")) {
0820         QSKIP("Skipping test which would fail due to an upstream bug, see https://bugs.freedesktop.org/show_bug.cgi?id=31242");
0821     }
0822 
0823     if (mimeType == "application/vnd.oasis.opendocument.presentation" && mimePatterns.contains("*.fodp")) {
0824         QSKIP("Skipping test which would fail due to an upstream bug, see https://bugs.freedesktop.org/show_bug.cgi?id=31242");
0825     }
0826 
0827     // shared-mime-info 0.30 adds *,v to text/plain, let's add it from this test so that it works
0828     // with older versions too.
0829     if (mimeType == "text/plain" && !mimePatterns.contains("*,v")) {
0830         mimePatterns.append("*,v");
0831     }
0832     mimePatterns.sort();
0833     // Not robust enough, other packages can add additional patterns, like libfm.xml adds *.inf to text/plain
0834     //QCOMPARE(mimePatterns.join(";"), expectedPatterns.join(";"));
0835     Q_FOREACH (const QString &expected, expectedPatterns) {
0836         QVERIFY2(mimePatterns.contains(expected), qPrintable(mimeType + " did not have pattern " + expected));
0837     }
0838 
0839     QCOMPARE(mime->mainExtension(), mainExtension);
0840 }
0841 
0842 void KMimeTypeTest::testExtractKnownExtension_data()
0843 {
0844     QTest::addColumn<QString>("fileName");
0845     QTest::addColumn<QString>("extension");
0846     QTest::newRow("simple extension") << "foo.pdf" << "pdf";
0847     QTest::newRow("filename has two extensions, last one matches") << "kpresenter.foo.kpt" << "kpt";
0848     QTest::newRow("filename has two extensions, pattern for both exist") << "foo.tar.bz2" << "tar.bz2";
0849     QTest::newRow("bz2 alone works too") << "foo.bz2" << "bz2";
0850 }
0851 
0852 void KMimeTypeTest::testExtractKnownExtension()
0853 {
0854     QFETCH(QString, fileName);
0855     QFETCH(QString, extension);
0856     QCOMPARE(KMimeType::extractKnownExtension(fileName), extension);
0857 }
0858 
0859 struct LessMimeType_ByComment {
0860     bool operator()(const KMimeType::Ptr &lhs, const KMimeType::Ptr &rhs) const
0861     {
0862         return lhs->comment() < rhs->comment();
0863     }
0864 };
0865 
0866 void KMimeTypeTest::testSortByComment()
0867 {
0868     QBENCHMARK {
0869         KMimeType::List sortedList = KMimeType::allMimeTypes();
0870         std::sort(sortedList.begin(), sortedList.end(), LessMimeType_ByComment());
0871     }
0872 }
0873 
0874 void KMimeTypeTest::testFromThread()
0875 {
0876     // Some simple tests to test more API from testThreads without using _data()
0877     KMimeType::Ptr mime = KMimeType::mimeType("application/pdf");
0878     QVERIFY(mime);
0879     QCOMPARE(mime->mainExtension(), QString::fromLatin1(".pdf"));
0880 }
0881 
0882 void KMimeTypeTest::testThreads()
0883 {
0884     QThreadPool::globalInstance()->setMaxThreadCount(20);
0885     // Note that data-based tests cannot be used here (QTest::fetchData asserts).
0886     QList<QFuture<void> > futures;
0887     futures << QtConcurrent::run(this, &KMimeTypeTest::testFindByUrl);
0888     futures << QtConcurrent::run(this, &KMimeTypeTest::testFindByFileContent);
0889     futures << QtConcurrent::run(this, &KMimeTypeTest::testFindByNameAndContent);
0890     futures << QtConcurrent::run(this, &KMimeTypeTest::testFindByPathWithContent);
0891     futures << QtConcurrent::run(this, &KMimeTypeTest::testAllMimeTypes);
0892     futures << QtConcurrent::run(this, &KMimeTypeTest::testAlias);
0893     futures << QtConcurrent::run(this, &KMimeTypeTest::testMimeTypeParent);
0894     futures << QtConcurrent::run(this, &KMimeTypeTest::testPreferredService);
0895     futures << QtConcurrent::run(this, &KMimeTypeTest::testFromThread);
0896     kDebug() << "Joining all threads";
0897     Q_FOREACH (QFuture<void> f, futures) { // krazy:exclude=foreach
0898         f.waitForFinished();
0899     }
0900 }
0901 
0902 void KMimeTypeTest::testProperties()
0903 {
0904     KMimeType::Ptr pngMimeType = KMimeType::mimeType("image/png");
0905 #if 0 // API removed
0906     QVariant comment = pngMimeType->property("Comment");
0907     QVariant patterns = pngMimeType->property("Patterns");
0908 
0909     QCOMPARE(comment.toString(), pngMimeType->comment());
0910     QCOMPARE(patterns.toStringList(), pngMimeType->patterns());
0911 #endif
0912 }
0913 
0914 void KMimeTypeTest::testIsNull()
0915 {
0916     KMimeType::Ptr ptr;
0917     QVERIFY(ptr.isNull());
0918     ptr = KMimeType::mimeType(QStringLiteral("image/jpeg"));
0919     QVERIFY(!ptr.isNull());
0920 }