File indexing completed on 2024-03-24 15:33:31

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2006 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kfileitemtest.h"
0009 #include <QTest>
0010 #include <kfileitem.h>
0011 #include <kfileitemlistproperties.h>
0012 
0013 #include "kiotesthelper.h"
0014 #include <KConfigGroup>
0015 #include <KDesktopFile>
0016 #include <KUser>
0017 #include <QTemporaryDir>
0018 #include <QTemporaryFile>
0019 
0020 #include <KProtocolInfo>
0021 #include <QMimeDatabase>
0022 
0023 QTEST_MAIN(KFileItemTest)
0024 
0025 void KFileItemTest::initTestCase()
0026 {
0027     QStandardPaths::setTestModeEnabled(true);
0028 }
0029 
0030 void KFileItemTest::testPermissionsString()
0031 {
0032     // Directory
0033     QTemporaryDir tempDir;
0034     KFileItem dirItem(QUrl::fromLocalFile(tempDir.path() + '/'));
0035     QCOMPARE((uint)dirItem.permissions(), (uint)0700);
0036     QCOMPARE(dirItem.permissionsString(), QStringLiteral("drwx------"));
0037     QVERIFY(dirItem.isReadable());
0038 
0039     // File
0040     QFile file(tempDir.path() + "/afile");
0041     QVERIFY(file.open(QIODevice::WriteOnly));
0042     file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadOther); // 0604
0043     KFileItem fileItem(QUrl::fromLocalFile(file.fileName()), QString(), KFileItem::Unknown);
0044     QCOMPARE((uint)fileItem.permissions(), (uint)0604);
0045     QCOMPARE(fileItem.permissionsString(), QStringLiteral("-rw----r--"));
0046     QVERIFY(fileItem.isReadable());
0047 
0048     // Symlink to file
0049     QString symlink = tempDir.path() + "/asymlink";
0050     QVERIFY(file.link(symlink));
0051     QUrl symlinkUrl = QUrl::fromLocalFile(symlink);
0052     KFileItem symlinkItem(symlinkUrl, QString(), KFileItem::Unknown);
0053     QCOMPARE((uint)symlinkItem.permissions(), (uint)0604);
0054     // This is a bit different from "ls -l": we get the 'l' but we see the permissions of the target.
0055     // This is actually useful though; the user sees it's a link, and can check if he can read the [target] file.
0056     QCOMPARE(symlinkItem.permissionsString(), QStringLiteral("lrw----r--"));
0057     QVERIFY(symlinkItem.isReadable());
0058 
0059     // Symlink to directory (#162544)
0060     QVERIFY(QFile::remove(symlink));
0061     QVERIFY(QFile(tempDir.path() + '/').link(symlink));
0062     KFileItem symlinkToDirItem(symlinkUrl, QString(), KFileItem::Unknown);
0063     QCOMPARE((uint)symlinkToDirItem.permissions(), (uint)0700);
0064     QCOMPARE(symlinkToDirItem.permissionsString(), QStringLiteral("lrwx------"));
0065 }
0066 
0067 void KFileItemTest::testNull()
0068 {
0069     KFileItem null;
0070     QVERIFY(null.isNull());
0071     KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/")), QString(), KFileItem::Unknown);
0072     QVERIFY(!fileItem.isNull());
0073     null = fileItem; // ok, now 'null' isn't so null anymore
0074     QVERIFY(!null.isNull());
0075     QVERIFY(null.isReadable());
0076     QVERIFY(!null.isHidden());
0077 }
0078 
0079 void KFileItemTest::testDoesNotExist()
0080 {
0081     KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/doesnotexist")), QString(), KFileItem::Unknown);
0082     QVERIFY(!fileItem.isNull());
0083     QVERIFY(!fileItem.isReadable());
0084     QVERIFY(fileItem.user().isEmpty());
0085     QVERIFY(fileItem.group().isEmpty());
0086 }
0087 
0088 void KFileItemTest::testDetach()
0089 {
0090     KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown);
0091     QCOMPARE(fileItem.name(), QStringLiteral("one"));
0092     KFileItem fileItem2(fileItem);
0093     QVERIFY(fileItem == fileItem2);
0094     QVERIFY(fileItem.d == fileItem2.d);
0095     fileItem2.setName(QStringLiteral("two"));
0096     QCOMPARE(fileItem2.name(), QStringLiteral("two"));
0097     QCOMPARE(fileItem.name(), QStringLiteral("one")); // it detached
0098     QVERIFY(fileItem == fileItem2);
0099     QVERIFY(fileItem.d != fileItem2.d);
0100 
0101     fileItem = fileItem2;
0102     QCOMPARE(fileItem.name(), QStringLiteral("two"));
0103     QVERIFY(fileItem == fileItem2);
0104     QVERIFY(fileItem.d == fileItem2.d);
0105     QVERIFY(!(fileItem != fileItem2));
0106 }
0107 
0108 void KFileItemTest::testMove()
0109 {
0110     // Test move constructor
0111     {
0112         KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown);
0113         QCOMPARE(fileItem.name(), QStringLiteral("one"));
0114         KFileItem fileItem2(std::move(fileItem));
0115         QCOMPARE(fileItem2.name(), QStringLiteral("one"));
0116     }
0117 
0118     // Test move assignment
0119     {
0120         KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown);
0121         QCOMPARE(fileItem.name(), QStringLiteral("one"));
0122         KFileItem fileItem2(QUrl::fromLocalFile(QStringLiteral("/two")), QString(), KFileItem::Unknown);
0123         fileItem2 = std::move(fileItem);
0124         QCOMPARE(fileItem2.name(), QStringLiteral("one"));
0125     }
0126 
0127     // Now to test some value changes to make sure moving works as intended.
0128     KFileItem fileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString(), KFileItem::Unknown);
0129     QCOMPARE(fileItem.name(), QStringLiteral("one"));
0130     fileItem.setUrl(QUrl::fromLocalFile(QStringLiteral("/two")));
0131     QCOMPARE(fileItem.name(), QStringLiteral("two"));
0132 
0133     // Move fileitem to fileItem2, it should now contain everything fileItem had.
0134     // Just testing a property to make sure it does.
0135     KFileItem fileItem2(std::move(fileItem));
0136     QCOMPARE(fileItem2.name(), QStringLiteral("two"));
0137 }
0138 
0139 void KFileItemTest::testMimeTypeCtor()
0140 {
0141     KFileItem fileItem;
0142 
0143     fileItem = KFileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString("inode/directory"));
0144     QVERIFY(fileItem.isDir());
0145     QVERIFY(fileItem.isMimeTypeKnown());
0146 
0147     fileItem = KFileItem(QUrl::fromLocalFile(QStringLiteral("/one")), QString("image/jpeg"));
0148     QVERIFY(!fileItem.isDir());
0149     QVERIFY(fileItem.isMimeTypeKnown());
0150 
0151     fileItem = KFileItem(QUrl::fromLocalFile(QStringLiteral("/one.txt")), QString("inode/directory"));
0152     QVERIFY(fileItem.isDir());
0153     QVERIFY(fileItem.isMimeTypeKnown());
0154 
0155     fileItem = KFileItem(QUrl::fromLocalFile(QStringLiteral("/one.txt")), QString(" "));
0156     QVERIFY(!fileItem.isMimeTypeKnown());
0157 }
0158 
0159 void KFileItemTest::testBasic()
0160 {
0161     QTemporaryFile file;
0162     QVERIFY(file.open());
0163     QFile fileObj(file.fileName());
0164     QVERIFY(fileObj.open(QIODevice::WriteOnly));
0165     fileObj.write(QByteArray("Hello"));
0166     fileObj.close();
0167 
0168     QUrl url = QUrl::fromLocalFile(file.fileName());
0169     KFileItem fileItem(url, QString(), KFileItem::Unknown);
0170     QCOMPARE(fileItem.text(), url.fileName());
0171     QVERIFY(fileItem.isLocalFile());
0172     QCOMPARE(fileItem.localPath(), url.toLocalFile());
0173     QCOMPARE(fileItem.size(), KIO::filesize_t(5));
0174     QVERIFY(fileItem.linkDest().isEmpty());
0175     QVERIFY(!fileItem.isHidden());
0176     QVERIFY(fileItem.isReadable());
0177     QVERIFY(fileItem.isWritable());
0178     QVERIFY(fileItem.isFile());
0179     QVERIFY(!fileItem.isDir());
0180     QVERIFY(!fileItem.isDesktopFile());
0181 #ifndef Q_OS_WIN
0182     QCOMPARE(fileItem.user(), KUser().loginName());
0183 #endif
0184 }
0185 
0186 void KFileItemTest::testRootDirectory()
0187 {
0188     const QString rootPath = QDir::rootPath();
0189     QUrl url = QUrl::fromLocalFile(rootPath);
0190     KIO::UDSEntry entry;
0191     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("."));
0192     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0193     KFileItem fileItem(entry, url);
0194     QCOMPARE(fileItem.text(), QStringLiteral("."));
0195     QVERIFY(fileItem.isLocalFile());
0196     QCOMPARE(fileItem.localPath(), url.toLocalFile());
0197     QVERIFY(fileItem.linkDest().isEmpty());
0198     QVERIFY(!fileItem.isHidden());
0199     QVERIFY(!fileItem.isFile());
0200     QVERIFY(fileItem.isDir());
0201     QVERIFY(!fileItem.isDesktopFile());
0202 }
0203 
0204 void KFileItemTest::testHiddenFile()
0205 {
0206     QTemporaryDir tempDir;
0207     QFile file(tempDir.path() + "/.hiddenfile");
0208     QVERIFY(file.open(QIODevice::WriteOnly));
0209     KFileItem fileItem(QUrl::fromLocalFile(file.fileName()), QString(), KFileItem::Unknown);
0210     QCOMPARE(fileItem.text(), QStringLiteral(".hiddenfile"));
0211     QVERIFY(fileItem.isLocalFile());
0212     QVERIFY(fileItem.isHidden());
0213 }
0214 
0215 void KFileItemTest::testMimeTypeOnDemand()
0216 {
0217     QTemporaryFile file;
0218     QVERIFY(file.open());
0219 
0220     {
0221         KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0222         fileItem.setDelayedMimeTypes(true);
0223         QVERIFY(fileItem.currentMimeType().isDefault());
0224         QVERIFY(!fileItem.isMimeTypeKnown());
0225         QVERIFY(!fileItem.isFinalIconKnown());
0226         // qDebug() << fileItem.determineMimeType().name();
0227         QCOMPARE(fileItem.determineMimeType().name(), QStringLiteral("application/x-zerosize"));
0228         QCOMPARE(fileItem.mimetype(), QStringLiteral("application/x-zerosize"));
0229         QVERIFY(fileItem.isMimeTypeKnown());
0230         QVERIFY(fileItem.isFinalIconKnown());
0231     }
0232 
0233     {
0234         // Calling mimeType directly also does MIME type determination
0235         KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0236         fileItem.setDelayedMimeTypes(true);
0237         QVERIFY(!fileItem.isMimeTypeKnown());
0238         QCOMPARE(fileItem.mimetype(), QStringLiteral("application/x-zerosize"));
0239         QVERIFY(fileItem.isMimeTypeKnown());
0240     }
0241 
0242     {
0243         // Calling overlays should NOT do MIME type determination (#237668)
0244         KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0245         fileItem.setDelayedMimeTypes(true);
0246         QVERIFY(!fileItem.isMimeTypeKnown());
0247         fileItem.overlays();
0248         QVERIFY(!fileItem.isMimeTypeKnown());
0249     }
0250 
0251     {
0252         QTemporaryFile file;
0253         QVERIFY(file.open());
0254         // Check whether mime-magic is used.
0255         // No known extension, so it should be used by determineMimeType.
0256         file.write(QByteArray("%PDF-"));
0257         QString fileName = file.fileName();
0258         QVERIFY(!fileName.isEmpty());
0259         file.close();
0260         KFileItem fileItem(QUrl::fromLocalFile(fileName));
0261         fileItem.setDelayedMimeTypes(true);
0262         QCOMPARE(fileItem.currentMimeType().name(), QLatin1String("application/octet-stream"));
0263         QVERIFY(fileItem.currentMimeType().isValid());
0264         QVERIFY(fileItem.currentMimeType().isDefault());
0265         QVERIFY(!fileItem.isMimeTypeKnown());
0266         QCOMPARE(fileItem.determineMimeType().name(), QStringLiteral("application/pdf"));
0267         QCOMPARE(fileItem.mimetype(), QStringLiteral("application/pdf"));
0268     }
0269 
0270     {
0271         QTemporaryFile file(QDir::tempPath() + QLatin1String("/kfileitemtest_XXXXXX.txt"));
0272         QVERIFY(file.open());
0273         // Check whether mime-magic is used.
0274         // Known extension, so it should NOT be used.
0275         file.write(QByteArray("<smil"));
0276         QString fileName = file.fileName();
0277         QVERIFY(!fileName.isEmpty());
0278         file.close();
0279         KFileItem fileItem(QUrl::fromLocalFile(fileName));
0280         fileItem.setDelayedMimeTypes(true);
0281         QCOMPARE(fileItem.currentMimeType().name(), QStringLiteral("text/plain"));
0282         QVERIFY(fileItem.isMimeTypeKnown());
0283         QCOMPARE(fileItem.determineMimeType().name(), QStringLiteral("text/plain"));
0284         QCOMPARE(fileItem.mimetype(), QStringLiteral("text/plain"));
0285 
0286         // And if the MIME type is not on demand?
0287         KFileItem fileItem2(QUrl::fromLocalFile(fileName));
0288         QCOMPARE(fileItem2.currentMimeType().name(), QStringLiteral("text/plain")); // XDG says: application/smil; but can't sniff all files so this can't work
0289         QVERIFY(fileItem2.isMimeTypeKnown());
0290     }
0291 }
0292 
0293 void KFileItemTest::testCmp()
0294 {
0295     QTemporaryFile file;
0296     QVERIFY(file.open());
0297 
0298     KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0299     fileItem.setDelayedMimeTypes(true);
0300     KFileItem fileItem2(QUrl::fromLocalFile(file.fileName()));
0301     QVERIFY(fileItem == fileItem2); // created independently, but still 'equal'
0302     QVERIFY(fileItem.d != fileItem2.d);
0303     QVERIFY(!(fileItem != fileItem2));
0304     QVERIFY(fileItem.cmp(fileItem2));
0305 }
0306 
0307 void KFileItemTest::testCmpAndInit()
0308 {
0309     QTemporaryDir tempDir;
0310     KFileItem dirItem(QUrl::fromLocalFile(tempDir.path()));
0311     QVERIFY(dirItem.isDir()); // this calls init()
0312 
0313     KFileItem dirItem2(QUrl::fromLocalFile(tempDir.path()));
0314     // not yet init() called on dirItem2, but must be equal
0315     // compare init()ialized to un-init()ialized KFileItem
0316     QVERIFY(dirItem.cmp(dirItem2));
0317     QVERIFY(dirItem2.isDir());
0318     QVERIFY(dirItem.cmp(dirItem2));
0319     QVERIFY(dirItem == dirItem2);
0320     QVERIFY(dirItem.d != dirItem2.d);
0321     QVERIFY(!(dirItem != dirItem2));
0322 
0323     // now the other way around, compare un-init()ialized to init()ialized KFileItem
0324     KFileItem dirItem3(QUrl::fromLocalFile(tempDir.path()));
0325     // not yet init() called on dirItem3, but must be equal
0326     QVERIFY(dirItem3.cmp(dirItem));
0327     QVERIFY(dirItem3.isDir());
0328     QVERIFY(dirItem3.cmp(dirItem));
0329     QVERIFY(dirItem == dirItem3);
0330     QVERIFY(dirItem.d != dirItem3.d);
0331     QVERIFY(!(dirItem != dirItem3));
0332 }
0333 
0334 void KFileItemTest::testCmpByUrl()
0335 {
0336     const QUrl nulUrl;
0337     const QUrl url = QUrl::fromLocalFile(QStringLiteral("1foo"));
0338     const QUrl url2 = QUrl::fromLocalFile(QStringLiteral("fo1"));
0339     const QUrl url3 = QUrl::fromLocalFile(QStringLiteral("foo"));
0340     KFileItem nulFileItem;
0341     KFileItem nulFileItem2(nulUrl);
0342     KFileItem fileItem(url);
0343     KFileItem fileItem2(url2);
0344     KFileItem fileItem3(url3);
0345 
0346     // an invalid KFileItem is considered equal to any other invalid KFileItem or invalid QUrl.
0347     QVERIFY(!(nulFileItem < nulFileItem));
0348     QVERIFY(!(nulFileItem < nulFileItem2));
0349     QVERIFY(!(nulFileItem2 < nulFileItem));
0350     QVERIFY(!(nulFileItem < nulUrl));
0351     // an invalid KFileItem is considered less than any valid KFileItem.
0352     QVERIFY(nulFileItem < fileItem);
0353     // a valid KFileItem is not less than an invalid KFileItem or invalid QUrl
0354     QVERIFY(!(fileItem < nulUrl));
0355     QVERIFY(!(fileItem < nulFileItem));
0356     QVERIFY(!(fileItem < nulFileItem2));
0357 
0358     QVERIFY(fileItem < fileItem2);
0359     QVERIFY(fileItem < url2);
0360     QVERIFY(!(fileItem2 < fileItem));
0361     QVERIFY(fileItem2 < fileItem3);
0362     QVERIFY(fileItem < url3);
0363     QVERIFY(!(fileItem3 < fileItem2));
0364     QVERIFY(!(fileItem3 < fileItem));
0365     // Must be false as they are considered equal
0366     QVERIFY(!(fileItem < fileItem));
0367     QVERIFY(!(fileItem < url));
0368 }
0369 
0370 void KFileItemTest::testRename()
0371 {
0372     KIO::UDSEntry entry;
0373     const QString origName = QStringLiteral("foo");
0374     entry.fastInsert(KIO::UDSEntry::UDS_NAME, origName);
0375     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0376     KFileItem fileItem(entry, QUrl::fromLocalFile(QStringLiteral("/dir/foo")));
0377     QCOMPARE(fileItem.name(), origName);
0378     QCOMPARE(fileItem.text(), origName);
0379     const QString newName = QStringLiteral("FiNeX_rocks");
0380     fileItem.setName(newName);
0381     QCOMPARE(fileItem.name(), newName);
0382     QCOMPARE(fileItem.text(), newName);
0383     QCOMPARE(fileItem.entry().stringValue(KIO::UDSEntry::UDS_NAME), newName); // #195385
0384 }
0385 
0386 void KFileItemTest::testRefresh()
0387 {
0388     QTemporaryDir tempDir;
0389     QFileInfo dirInfo(tempDir.path());
0390     // Refresh on a dir
0391     KFileItem dirItem(QUrl::fromLocalFile(tempDir.path()));
0392     QVERIFY(dirItem.isDir());
0393     QVERIFY(dirItem.entry().isDir());
0394     QDateTime lastModified = dirInfo.lastModified();
0395     // Qt 5.8 adds milliseconds (but UDSEntry has no support for that)
0396     lastModified = lastModified.addMSecs(-lastModified.time().msec());
0397     QCOMPARE(dirItem.time(KFileItem::ModificationTime), lastModified);
0398     dirItem.refresh();
0399     QVERIFY(dirItem.isDir());
0400     QVERIFY(dirItem.entry().isDir());
0401     QCOMPARE(dirItem.time(KFileItem::ModificationTime), lastModified);
0402 
0403     // Refresh on a file
0404     QFile file(tempDir.path() + "/afile");
0405     QVERIFY(file.open(QIODevice::WriteOnly));
0406     file.write("Hello world\n");
0407     file.close();
0408     QFileInfo fileInfo(file.fileName());
0409     const KIO::filesize_t expectedSize = 12;
0410     QCOMPARE(KIO::filesize_t(fileInfo.size()), expectedSize);
0411     file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadOther); // 0604
0412     KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0413     QVERIFY(fileItem.isFile());
0414     QVERIFY(!fileItem.isLink());
0415     QCOMPARE(fileItem.size(), expectedSize);
0416 #ifndef Q_OS_WIN
0417     QCOMPARE(fileItem.user(), KUser().loginName());
0418 #endif
0419     // Qt 5.8 adds milliseconds (but UDSEntry has no support for that)
0420     lastModified = dirInfo.lastModified();
0421     // Truncate away the milliseconds...
0422     lastModified = lastModified.addMSecs(-lastModified.time().msec());
0423     // ...but it looks like the kernel rounds up when the msecs are .998 or .999,
0424     // so add a bit of tolerance
0425     auto expectedLastModified = lastModified;
0426     if (fileItem.time(KFileItem::ModificationTime) != lastModified && fileItem.time(KFileItem::ModificationTime) == lastModified.addSecs(1)) {
0427         expectedLastModified = expectedLastModified.addSecs(1);
0428     }
0429     QCOMPARE(fileItem.time(KFileItem::ModificationTime), expectedLastModified);
0430     fileItem.refresh();
0431     QVERIFY(fileItem.isFile());
0432     QVERIFY(!fileItem.isLink());
0433     QCOMPARE(fileItem.size(), expectedSize);
0434 #ifndef Q_OS_WIN
0435     QCOMPARE(fileItem.user(), KUser().loginName());
0436 #endif
0437     QCOMPARE(fileItem.time(KFileItem::ModificationTime), expectedLastModified);
0438 
0439     // Refresh on a symlink to a file
0440     const QString symlink = tempDir.path() + "/asymlink";
0441     QVERIFY(file.link(symlink));
0442     QDateTime symlinkTime = QDateTime::currentDateTime().addSecs(-20);
0443     // we currently lose milliseconds....
0444     symlinkTime = symlinkTime.addMSecs(-symlinkTime.time().msec());
0445     setTimeStamp(symlink, symlinkTime); // differentiate link time and source file time
0446     const QUrl symlinkUrl = QUrl::fromLocalFile(symlink);
0447     KFileItem symlinkItem(symlinkUrl);
0448     QVERIFY(symlinkItem.isFile());
0449     QVERIFY(symlinkItem.isLink());
0450     QCOMPARE(symlinkItem.size(), expectedSize);
0451     QCOMPARE(symlinkItem.time(KFileItem::ModificationTime), symlinkTime);
0452     symlinkItem.refresh();
0453     QVERIFY(symlinkItem.isFile());
0454     QVERIFY(symlinkItem.isLink());
0455     QCOMPARE(symlinkItem.size(), expectedSize);
0456     QCOMPARE(symlinkItem.time(KFileItem::ModificationTime), symlinkTime);
0457 
0458     // Symlink to directory (#162544)
0459     QVERIFY(QFile::remove(symlink));
0460     QVERIFY(QFile(tempDir.path() + '/').link(symlink));
0461     KFileItem symlinkToDirItem(symlinkUrl);
0462     QVERIFY(symlinkToDirItem.isDir());
0463     QVERIFY(symlinkToDirItem.isLink());
0464     symlinkToDirItem.refresh();
0465     QVERIFY(symlinkToDirItem.isDir());
0466     QVERIFY(symlinkToDirItem.isLink());
0467 }
0468 
0469 void KFileItemTest::testDotDirectory()
0470 {
0471     QTemporaryDir tempDir;
0472     QFile file(tempDir.path() + "/.directory");
0473     QVERIFY(file.open(QIODevice::WriteOnly));
0474     file.write("[Desktop Entry]\nIcon=foo\nComment=com\n");
0475     file.close();
0476     {
0477         KFileItem fileItem(QUrl::fromLocalFile(tempDir.path() + '/'), QString(), KFileItem::Unknown);
0478         QVERIFY(fileItem.isLocalFile());
0479         QCOMPARE(fileItem.mimeComment(), QStringLiteral("com"));
0480         QCOMPARE(fileItem.iconName(), QStringLiteral("foo"));
0481     }
0482     // Test for calling iconName first, to trigger MIME type resolution
0483     {
0484         KFileItem fileItem(QUrl::fromLocalFile(tempDir.path()), QString(), KFileItem::Unknown);
0485         QVERIFY(fileItem.isLocalFile());
0486         QCOMPARE(fileItem.iconName(), QStringLiteral("foo"));
0487     }
0488 }
0489 
0490 void KFileItemTest::testDecodeFileName_data()
0491 {
0492     QTest::addColumn<QString>("filename");
0493     QTest::addColumn<QString>("expectedText");
0494 
0495     QTest::newRow("simple") << "filename"
0496                             << "filename";
0497     QTest::newRow("/ at end") << QString(QStringLiteral("foo") + QChar(0x2044)) << QString(QStringLiteral("foo") + QChar(0x2044));
0498     QTest::newRow("/ at begin") << QString(QChar(0x2044)) << QString(QChar(0x2044));
0499 }
0500 
0501 void KFileItemTest::testDecodeFileName()
0502 {
0503     QFETCH(QString, filename);
0504     QFETCH(QString, expectedText);
0505     QCOMPARE(KIO::decodeFileName(filename), expectedText);
0506 }
0507 
0508 void KFileItemTest::testEncodeFileName_data()
0509 {
0510     QTest::addColumn<QString>("text");
0511     QTest::addColumn<QString>("expectedFileName");
0512 
0513     QTest::newRow("simple") << "filename"
0514                             << "filename";
0515     QTest::newRow("/ at end") << "foo/" << QString(QStringLiteral("foo") + QChar(0x2044));
0516     QTest::newRow("/ at begin") << "/" << QString(QChar(0x2044));
0517 }
0518 
0519 void KFileItemTest::testEncodeFileName()
0520 {
0521     QFETCH(QString, text);
0522     QFETCH(QString, expectedFileName);
0523     QCOMPARE(KIO::encodeFileName(text), expectedFileName);
0524 }
0525 
0526 void KFileItemTest::testListProperties_data()
0527 {
0528     QTest::addColumn<QString>("itemDescriptions");
0529     QTest::addColumn<bool>("expectedReading");
0530     QTest::addColumn<bool>("expectedDeleting");
0531     QTest::addColumn<bool>("expectedIsLocal");
0532     QTest::addColumn<bool>("expectedIsDirectory");
0533     QTest::addColumn<bool>("expectedIsFile");
0534     QTest::addColumn<QString>("expectedMimeType");
0535     QTest::addColumn<QString>("expectedMimeGroup");
0536 
0537     /* clang-format off */
0538     QTest::newRow("one file") << "f" << true << true << true << false << true << "text/plain" << "text";
0539     QTest::newRow("one dir") << "d" << true << true << true << true << false << "inode/directory" << "inode";
0540     QTest::newRow("root dir") << "/" << true << false << true << true << false << "inode/directory" << "inode";
0541     QTest::newRow("file+dir") << "fd" << true << true << true << false << false << "" << "";
0542     QTest::newRow("two dirs") << "dd" << true << true << true << true << false << "inode/directory" << "inode";
0543     QTest::newRow("dir+root dir") << "d/" << true << false << true << true << false << "inode/directory" << "inode";
0544     QTest::newRow("two (text+html) files") << "ff" << true << true << true << false << true << "" << "text";
0545     QTest::newRow("three (text+html+empty) files") << "fff" << true << true << true << false << true << "" << "";
0546     QTest::newRow("http url") << "h" << true << true /*says kio_http...*/ << false << false << true << "application/octet-stream" << "application";
0547     QTest::newRow("2 http urls") << "hh" << true << true /*says kio_http...*/ << false << false << true << "application/octet-stream" << "application";
0548     /* clang-format on */
0549 }
0550 
0551 void KFileItemTest::testListProperties()
0552 {
0553     QFETCH(QString, itemDescriptions);
0554     QFETCH(bool, expectedReading);
0555     QFETCH(bool, expectedDeleting);
0556     QFETCH(bool, expectedIsLocal);
0557     QFETCH(bool, expectedIsDirectory);
0558     QFETCH(bool, expectedIsFile);
0559     QFETCH(QString, expectedMimeType);
0560     QFETCH(QString, expectedMimeGroup);
0561 
0562     QTemporaryDir tempDir;
0563     QDir baseDir(tempDir.path());
0564     KFileItemList items;
0565     for (int i = 0; i < itemDescriptions.size(); ++i) {
0566         QString fileName = tempDir.path() + "/file" + QString::number(i);
0567         switch (itemDescriptions[i].toLatin1()) {
0568         case 'f': {
0569             if (i == 1) { // 2nd file is html
0570                 fileName += QLatin1String(".html");
0571             }
0572             QFile file(fileName);
0573             QVERIFY(file.open(QIODevice::WriteOnly));
0574             if (i == 0) {
0575                 file.write("Hello");
0576             } else if (i == 1) {
0577                 file.write("<html>");
0578             } // i == 2: leave the file empty
0579             file.close();
0580             KFileItem item(QUrl::fromLocalFile(fileName), QString(), KFileItem::Unknown);
0581             if (i == 0) {
0582                 QCOMPARE(item.mimetype(), "text/plain");
0583             } else if (i == 1) {
0584                 QCOMPARE(item.mimetype(), "text/html");
0585             } else if (i == 2) {
0586                 QCOMPARE(item.mimetype(), "application/x-zerosize");
0587             }
0588             items.push_back(std::move(item));
0589             break;
0590         }
0591         case 'd':
0592             QVERIFY(baseDir.mkdir(fileName));
0593             items << KFileItem(QUrl::fromLocalFile(fileName), QString(), KFileItem::Unknown);
0594             break;
0595         case '/':
0596             items << KFileItem(QUrl::fromLocalFile(QStringLiteral("/")), QString(), KFileItem::Unknown);
0597             break;
0598         case 'h':
0599             items << KFileItem(QUrl(QStringLiteral("http://www.kde.org")), QString(), KFileItem::Unknown);
0600             break;
0601         default:
0602             QVERIFY(false);
0603         }
0604     }
0605     KFileItemListProperties props(items);
0606     QCOMPARE(props.supportsReading(), expectedReading);
0607     QCOMPARE(props.supportsDeleting(), expectedDeleting);
0608     QCOMPARE(props.isLocal(), expectedIsLocal);
0609     QCOMPARE(props.isDirectory(), expectedIsDirectory);
0610     QCOMPARE(props.isFile(), expectedIsFile);
0611     QCOMPARE(props.mimeType(), expectedMimeType);
0612     QCOMPARE(props.mimeGroup(), expectedMimeGroup);
0613 }
0614 
0615 void KFileItemTest::testIconNameForUrl_data()
0616 {
0617     QTest::addColumn<QUrl>("url");
0618     QTest::addColumn<QString>("expectedIcon");
0619 
0620     QTest::newRow("root") << QUrl("file:/") << "inode-directory"; // the icon comes from KFileItem
0621     if (QFile::exists(QStringLiteral("/tmp"))) {
0622         QTest::newRow("subdir") << QUrl::fromLocalFile("/tmp") << "folder-temp";
0623     }
0624 
0625     QTest::newRow("home") << QUrl::fromLocalFile(QDir::homePath()) << "user-home";
0626     const QString moviesPath = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation).constFirst();
0627     if (QFileInfo::exists(moviesPath)) {
0628         QTest::newRow("videos") << QUrl::fromLocalFile(moviesPath) << (moviesPath == QDir::homePath() ? "user-home" : "folder-videos");
0629     }
0630 
0631     QTest::newRow("empty") << QUrl() << "unknown";
0632     QTest::newRow("relative") << QUrl("foo") << "unknown";
0633     QTest::newRow("tilde") << QUrl("~") << "unknown";
0634 
0635     QTest::newRow("unknownscheme folder") << QUrl("unknownscheme:/") << "inode-directory";
0636     QTest::newRow("unknownscheme file") << QUrl("unknownscheme:/test") << "application-octet-stream";
0637 
0638     QTest::newRow("trash:/ itself") << QUrl("trash:/") << "user-trash-full";
0639     QTest::newRow("folder under trash:/") << QUrl("trash:/folder/") << "inode-directory";
0640     QTest::newRow("file under trash:/") << QUrl("trash:/test") << "application-octet-stream";
0641     QTest::newRow("image file under trash:/") << QUrl("trash:/test.png") << "image-png";
0642 
0643     QTest::newRow("https scheme") << QUrl("https://kde.org/") << "text-html";
0644 
0645     if (KProtocolInfo::isKnownProtocol("smb")) {
0646         QTest::newRow("smb root") << QUrl("smb:/") << "network-workgroup";
0647         QTest::newRow("smb unknown file") << QUrl("smb:/test") << "network-workgroup";
0648         QTest::newRow("smb directory/") << QUrl("smb:/unknown/") << "inode-directory";
0649         QTest::newRow("smb image file") << QUrl("smb:/test.png") << "image-png";
0650     }
0651 }
0652 
0653 void KFileItemTest::testIconNameForUrl()
0654 {
0655     QFETCH(QUrl, url);
0656     QFETCH(QString, expectedIcon);
0657 
0658     if (KIO::iconNameForUrl(url) != expectedIcon) {
0659         qDebug() << url;
0660         QCOMPARE(KIO::iconNameForUrl(url), expectedIcon);
0661     }
0662 }
0663 
0664 void KFileItemTest::testMimetypeForRemoteFolder()
0665 {
0666     KIO::UDSEntry entry;
0667     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo"));
0668     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0669     QUrl url(QStringLiteral("smb://remoteFolder/foo"));
0670     KFileItem fileItem(entry, url);
0671 
0672     QCOMPARE(fileItem.mimetype(), QStringLiteral("inode/directory"));
0673 }
0674 
0675 void KFileItemTest::testMimetypeForRemoteFolderWithFileType()
0676 {
0677     QString udsMimeType = QStringLiteral("application/x-smb-workgroup");
0678     QVERIFY2(QMimeDatabase().mimeTypeForName(udsMimeType).isValid(),
0679              qPrintable(QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation).join(':'))); // kcoreaddons installed? XDG_DATA_DIRS set?
0680     KIO::UDSEntry entry;
0681     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo"));
0682     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0683     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, udsMimeType);
0684 
0685     QUrl url(QStringLiteral("smb://remoteFolder/foo"));
0686     KFileItem fileItem(entry, url);
0687 
0688     QCOMPARE(fileItem.mimetype(), udsMimeType);
0689 }
0690 
0691 void KFileItemTest::testCurrentMimetypeForRemoteFolder()
0692 {
0693     KIO::UDSEntry entry;
0694     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo"));
0695     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0696     QUrl url(QStringLiteral("smb://remoteFolder/foo"));
0697     KFileItem fileItem(entry, url);
0698 
0699     QCOMPARE(fileItem.currentMimeType().name(), QStringLiteral("inode/directory"));
0700 }
0701 
0702 void KFileItemTest::testCurrentMimetypeForRemoteFolderWithFileType()
0703 {
0704     QString udsMimeType = QStringLiteral("application/x-smb-workgroup");
0705     KIO::UDSEntry entry;
0706     entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral("foo"));
0707     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0708     entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, udsMimeType);
0709 
0710     QUrl url(QStringLiteral("smb://remoteFolder/foo"));
0711     KFileItem fileItem(entry, url);
0712 
0713     QCOMPARE(fileItem.currentMimeType().name(), udsMimeType);
0714 }
0715 
0716 void KFileItemTest::testIconNameForCustomFolderIcons()
0717 {
0718     // Custom folder icons should be displayed (bug 350612)
0719 
0720     const QString iconName = QStringLiteral("folder-music");
0721 
0722     QTemporaryDir tempDir;
0723     const QUrl url = QUrl::fromLocalFile(tempDir.path());
0724     KDesktopFile cfg(tempDir.path() + QLatin1String("/.directory"));
0725     cfg.desktopGroup().writeEntry("Icon", iconName);
0726     cfg.sync();
0727 
0728     KIO::UDSEntry entry;
0729     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0730     KFileItem fileItem(entry, url);
0731 
0732     QCOMPARE(fileItem.iconName(), iconName);
0733 }
0734 
0735 void KFileItemTest::testIconNameForStandardPath()
0736 {
0737     const QString iconName = QStringLiteral("folder-videos");
0738     const QUrl url = QUrl::fromLocalFile(QDir::homePath() + QLatin1String("/Videos"));
0739     QStandardPaths::setTestModeEnabled(true);
0740 
0741     KIO::UDSEntry entry;
0742     entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
0743     KFileItem fileItem(entry, url);
0744 
0745     QCOMPARE(fileItem.iconName(), iconName);
0746 }
0747 
0748 #ifndef Q_OS_WIN // user/group/other write permissions are not handled on windows
0749 
0750 void KFileItemTest::testIsReadable_data()
0751 {
0752     QTest::addColumn<int>("mode");
0753     QTest::addColumn<bool>("readable");
0754 
0755     QTest::newRow("fully-readable") << 0444 << true;
0756     QTest::newRow("user-readable") << 0400 << true;
0757     QTest::newRow("not-readable-by-us") << 0004 << false;
0758     QTest::newRow("not-readable-at-all") << 0000 << false;
0759 }
0760 
0761 void KFileItemTest::testIsReadable()
0762 {
0763     QFETCH(int, mode);
0764     QFETCH(bool, readable);
0765 
0766     QTemporaryFile file;
0767     QVERIFY(file.open());
0768     int ret = fchmod(file.handle(), (mode_t)mode);
0769     QCOMPARE(ret, 0);
0770 
0771     KFileItem fileItem(QUrl::fromLocalFile(file.fileName()));
0772     QCOMPARE(fileItem.isReadable(), readable);
0773 }
0774 
0775 // Restore permissions so that the QTemporaryDir cleanup can happen (taken from tst_qsavefile.cpp)
0776 class PermissionRestorer
0777 {
0778     Q_DISABLE_COPY(PermissionRestorer)
0779 public:
0780     explicit PermissionRestorer(const QString &path)
0781         : m_path(path)
0782     {
0783     }
0784     ~PermissionRestorer()
0785     {
0786         restore();
0787     }
0788 
0789     inline void restore()
0790     {
0791         QFile file(m_path);
0792 #ifdef Q_OS_UNIX
0793         file.setPermissions(QFile::Permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner));
0794 #else
0795         file.setPermissions(QFile::WriteOwner);
0796         file.remove();
0797 #endif
0798     }
0799 
0800 private:
0801     const QString m_path;
0802 };
0803 
0804 void KFileItemTest::testNonWritableDirectory()
0805 {
0806     // Given a directory with a file in it
0807     QTemporaryDir dir;
0808     QVERIFY2(dir.isValid(), qPrintable(dir.errorString()));
0809     QFile file(dir.path() + "/file1");
0810     QVERIFY(file.open(QIODevice::WriteOnly));
0811     QCOMPARE(file.write("Hello"), Q_INT64_C(5));
0812     file.close();
0813     // ... which is then made non-writable
0814     QVERIFY(QFile(dir.path()).setPermissions(QFile::ReadOwner | QFile::ExeOwner));
0815     PermissionRestorer permissionRestorer(dir.path());
0816 
0817     // When using KFileItemListProperties on the file
0818     const KFileItem item(QUrl::fromLocalFile(file.fileName()));
0819     KFileItemListProperties props(KFileItemList{item});
0820 
0821     // Then it should say moving is not supported
0822     QVERIFY(!props.supportsMoving());
0823     QVERIFY(props.supportsWriting()); // but we can write to the file itself
0824 }
0825 
0826 #endif // Q_OS_WIN
0827 
0828 #include "moc_kfileitemtest.cpp"