diff --git a/autotests/kcoredirlister_benchmark.cpp b/autotests/kcoredirlister_benchmark.cpp index 71fdea1b..f73d054f 100644 --- a/autotests/kcoredirlister_benchmark.cpp +++ b/autotests/kcoredirlister_benchmark.cpp @@ -1,552 +1,552 @@ /* This file is part of the KDE project Copyright (C) 2018 Jaime Torres This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include // BEGIN Global variables const QString fileNameArg = QLatin1String("/home/user/Folder1/SubFolder2/a%1.txt"); // to check with 10, 100, 1000, ... KFileItem const int maxPowerOfTen=4; // To use the same random list of names and url for all the tests QVector randInt[maxPowerOfTen]; // The same list of random integers for all the tests std::default_random_engine generator; // END Global variables /* This is to compare the old list API vs QMap API vs QHash API vs sorted list API in terms of performance for KcoreDirLister list of items. This benchmark assumes that KFileItem has the < operators. */ class kcoreDirListerEntryBenchmark : public QObject { Q_OBJECT public: kcoreDirListerEntryBenchmark() { // Fill randInt[i] with random numbers from 0 to (10^(i+1))-1 for (int i=0; i < maxPowerOfTen; ++i) { std::uniform_int_distribution distribution(0,pow(10,i+1)-1); // Fill the vector with consecutive numbers randInt[i].reserve(pow(10,i+1)); for (int j=0; j < pow(10,i+1); ++j) { randInt[i].append(j); } // And now scramble them a little bit for (int j=0; j < pow(10,i+1); ++j) { int rd1 = distribution(generator); int rd2 = distribution(generator); int swap = randInt[i].at(rd1); randInt[i].replace(rd1, randInt[i].at(rd2)); randInt[i].replace(rd2, swap); } // qDebug() << randInt[i]; } } private Q_SLOTS: void testCreateFiles_List_data(); void testCreateFiles_List(); void testFindByNameFiles_List_data(); void testFindByNameFiles_List(); void testFindByUrlFiles_List_data(); void testFindByUrlFiles_List(); void testFindByUrlAllFiles_List_data(); void testFindByUrlAllFiles_List(); void testCreateFiles_Map_data(); void testCreateFiles_Map(); void testFindByNameFiles_Map_data(); void testFindByNameFiles_Map(); void testFindByUrlFiles_Map_data(); void testFindByUrlFiles_Map(); void testFindByUrlAllFiles_Map_data(); void testFindByUrlAllFiles_Map(); void testCreateFiles_Hash_data(); void testCreateFiles_Hash(); void testFindByNameFiles_Hash_data(); void testFindByNameFiles_Hash(); void testFindByUrlFiles_Hash_data(); void testFindByUrlFiles_Hash(); void testFindByUrlAllFiles_Hash_data(); void testFindByUrlAllFiles_Hash(); void testCreateFiles_Binary_data(); void testCreateFiles_Binary(); void testFindByNameFiles_Binary_data(); void testFindByNameFiles_Binary(); void testFindByUrlFiles_Binary_data(); void testFindByUrlFiles_Binary(); void testFindByUrlAllFiles_Binary_data(); void testFindByUrlAllFiles_Binary(); }; //BEGIN Implementations //BEGIN List // List Implementation (without binary search) class ListImplementation { public: QList lstItems; public: void reserve(int size) { lstItems.reserve(size); } // This search must be fast also KFileItem findByName(const QString &fileName) const { const auto end = lstItems.cend(); for (auto it = lstItems.cbegin() ; it != end; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } // simulation of the search by Url in an existing lister (the slowest path) KFileItem findByUrl(const QUrl &_u) const { QUrl url(_u); url = url.adjusted(QUrl::StripTrailingSlash); const auto end = lstItems.cend(); for (auto it = lstItems.cbegin(); it != end; ++it) { if ((*it).url() == url) { return *it; } } return KFileItem(); } void clear() { lstItems.clear(); } void insert(int powerOfTen) { for (int x = 0; x < pow(10, powerOfTen+1); ++x) { QUrl u = QUrl::fromLocalFile(fileNameArg.arg(randInt[powerOfTen].at(x) )).adjusted(QUrl::StripTrailingSlash); KFileItem kfi (u, QStringLiteral("text/text")); lstItems.append(kfi); } } }; //END List //BEGIN QMap // Proposed Implementation using QMap class QMapImplementation { public: void reserve(int size) { Q_UNUSED(size); } KFileItem findByName(const QString &fileName) const { const auto itend = lstItems.cend(); for (auto it = lstItems.cbegin(); it != itend; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } // simulation of the search by Url in an existing lister (the slowest path) KFileItem findByUrl(const QUrl &_u) const { QUrl url(_u); url = url.adjusted(QUrl::StripTrailingSlash); auto it = lstItems.find(url); if (it != lstItems.end()) { return *it; } return KFileItem(); } void clear() { lstItems.clear(); } void insert(int powerOfTen) { for (int x = 0; x < pow(10, powerOfTen+1); ++x) { QUrl u = QUrl::fromLocalFile(fileNameArg.arg(randInt[powerOfTen].at(x) )).adjusted(QUrl::StripTrailingSlash); KFileItem kfi(u, QStringLiteral("text/text")); lstItems.insert(u, kfi); } } public: QMap lstItems; }; //END QMap //BEGIN QHash // Proposed Implementation using QHash class QHashImplementation { public: void reserve(int size) { lstItems.reserve(size); } KFileItem findByName(const QString &fileName) const { const auto itend = lstItems.cend(); for (auto it = lstItems.cbegin(); it != itend; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } // simulation of the search by Url in an existing lister (the slowest path) KFileItem findByUrl(const QUrl &_u) const { QUrl url(_u); url = url.adjusted(QUrl::StripTrailingSlash); auto it = lstItems.find(url); if (it != lstItems.end()) { return *it; } return KFileItem(); } void clear() { lstItems.clear(); } void insert(int powerOfTen) { for (int x = 0; x < pow(10, powerOfTen+1); ++x) { QUrl u = QUrl::fromLocalFile(fileNameArg.arg(randInt[powerOfTen].at(x) )).adjusted(QUrl::StripTrailingSlash); KFileItem kfi(u, QStringLiteral("text/text")); lstItems.insert(u, kfi); } } public: QHash lstItems; }; //END QHash //BEGIN BinaryList // Proposed Implementation using QList with ordered insert and binary search class BinaryListImplementation { public: QList lstItems; public: void reserve(int size) { lstItems.reserve(size); } KFileItem findByName(const QString &fileName) const { const auto itend = lstItems.cend(); for (auto it = lstItems.cbegin(); it != itend; ++it) { if ((*it).name() == fileName) { return *it; } } return KFileItem(); } // simulation of the search by Url in an existing lister (the slowest path) KFileItem findByUrl(const QUrl &_u) const { QUrl url(_u); url = url.adjusted(QUrl::StripTrailingSlash); auto it = std::lower_bound(lstItems.cbegin(), lstItems.cend(), url); if (it != lstItems.cend() && (*it).url() == url) { return *it; } return KFileItem(); } void clear() { lstItems.clear(); } // Add files in random order from the randInt vector void insert(int powerOfTen) { for (int x = 0; x < pow(10, powerOfTen+1); ++x) { QUrl u = QUrl::fromLocalFile(fileNameArg.arg(randInt[powerOfTen].at(x) )).adjusted(QUrl::StripTrailingSlash); KFileItem kfi(u, QStringLiteral("text/text")); auto it = std::lower_bound(lstItems.begin(), lstItems.end(), u); lstItems.insert(it, kfi); } } }; //END BinaryList //END Implementations //BEGIN templates template void fillNumberOfFiles() { QTest::addColumn("numberOfFiles"); for (int i=0; i < maxPowerOfTen; ++i) { // it shows numberOfFiles: 10, 100 or 1000 but the data is the power of ten - QTest::newRow( QString("%1").arg(pow(10, i+1)).toLatin1() ) << i; + QTest::newRow( QStringLiteral("%1").arg(pow(10, i+1)).toLatin1() ) << i; } } template void createFiles(int powerOfTen) { T data; const int numberOfFiles = pow(10, powerOfTen+1); data.reserve(numberOfFiles); QBENCHMARK { data.clear(); data.insert(powerOfTen); } QCOMPARE(data.lstItems.size(), numberOfFiles); } template void findByName(int powerOfTen) { T data; data.clear(); data.reserve(pow(10, powerOfTen+1)); data.insert(powerOfTen); QBENCHMARK { for (int i=0; i void findByUrl(int powerOfTen) { T data; data.clear(); data.reserve(pow(10, powerOfTen+1)); data.insert(powerOfTen); QBENCHMARK { for (int i=0; i void findByUrlAll(int powerOfTen) { T data; data.clear(); data.reserve(pow(10, powerOfTen+1)); data.insert(powerOfTen); QBENCHMARK { for (int i=0; i(); } void kcoreDirListerEntryBenchmark::testCreateFiles_List() { QFETCH(int, numberOfFiles); createFiles(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_List_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_List() { QFETCH(int, numberOfFiles); findByName(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_List_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_List() { QFETCH(int, numberOfFiles); findByUrl(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_List_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_List() { QFETCH(int, numberOfFiles); findByUrlAll(numberOfFiles); } void kcoreDirListerEntryBenchmark::testCreateFiles_Map_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testCreateFiles_Map() { QFETCH(int, numberOfFiles); createFiles(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Map_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Map() { QFETCH(int, numberOfFiles); findByName(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Map_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Map() { QFETCH(int, numberOfFiles); findByUrl(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Map_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Map() { QFETCH(int, numberOfFiles); findByUrlAll(numberOfFiles); } void kcoreDirListerEntryBenchmark::testCreateFiles_Hash_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testCreateFiles_Hash() { QFETCH(int, numberOfFiles); createFiles(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Hash_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Hash() { QFETCH(int, numberOfFiles); findByName(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Hash_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Hash() { QFETCH(int, numberOfFiles); findByUrl(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Hash_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Hash() { QFETCH(int, numberOfFiles); findByUrlAll(numberOfFiles); } void kcoreDirListerEntryBenchmark::testCreateFiles_Binary_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testCreateFiles_Binary() { QFETCH(int, numberOfFiles); createFiles(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Binary_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByNameFiles_Binary() { QFETCH(int, numberOfFiles); findByName(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Binary_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlFiles_Binary() { QFETCH(int, numberOfFiles); findByUrl(numberOfFiles); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Binary_data() { fillNumberOfFiles(); } void kcoreDirListerEntryBenchmark::testFindByUrlAllFiles_Binary() { QFETCH(int, numberOfFiles); findByUrlAll(numberOfFiles); } //END tests QTEST_MAIN(kcoreDirListerEntryBenchmark) #include "kcoredirlister_benchmark.moc" diff --git a/autotests/kfilecopytomenutest.cpp b/autotests/kfilecopytomenutest.cpp index 4a412706..7d557c63 100644 --- a/autotests/kfilecopytomenutest.cpp +++ b/autotests/kfilecopytomenutest.cpp @@ -1,183 +1,183 @@ /* This file is part of the KDE project Copyright (C) 2014 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "kiotesthelper.h" #include "jobuidelegatefactory.h" #include class KFileCopyToMenuTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { QStandardPaths::setTestModeEnabled(true); qputenv("KIOSLAVE_ENABLE_TESTMODE", "1"); // ensure the ioslaves call QStandardPaths::setTestModeEnabled too qputenv("KDE_FORK_SLAVES", "yes"); // to avoid a runtime dependency on klauncher QVERIFY(m_tempDir.isValid()); QVERIFY(m_tempDestDir.isValid()); QVERIFY(m_nonWritableTempDir.isValid()); QVERIFY(QFile(m_nonWritableTempDir.path()).setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::ExeOwner | QFile::ExeUser)); m_srcDir = m_tempDir.path(); m_destDir = m_tempDestDir.path(); - m_srcFile = m_srcDir + "/srcfile"; + m_srcFile = m_srcDir + QStringLiteral("/srcfile"); KIO::setDefaultJobUiDelegateExtension(nullptr); // no "skip" dialogs // Set a recent dir KConfigGroup recentDirsGroup(KSharedConfig::openConfig(), "kuick-copy"); m_recentDirs << m_destDir + QStringLiteral("/nonexistentsubdir") // will be action number count-3 << m_nonWritableTempDir.path() // will be action number count-2 << m_destDir; // will be action number count-1 recentDirsGroup.writeEntry("Paths", m_recentDirs); m_lastActionCount = 0; } void cleanupTestCase() { QVERIFY(QFile(m_nonWritableTempDir.path()).setPermissions(QFile::ReadOwner | QFile::ReadUser | QFile::WriteOwner | QFile::WriteUser | QFile::ExeOwner | QFile::ExeUser)); } // Before every test method, ensure the test file m_srcFile exists void init() { if (QFile::exists(m_srcFile)) { QVERIFY(QFileInfo(m_srcFile).isWritable()); } else { QFile srcFile(m_srcFile); QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString())); srcFile.write("Hello world\n"); } QVERIFY(QFileInfo(m_srcFile).isWritable()); } void shouldHaveParentWidget() { KFileCopyToMenu generator(&m_parentWidget); QCOMPARE(generator.parent(), &m_parentWidget); } void shouldAddActions() { KFileCopyToMenu generator(&m_parentWidget); QMenu menu; generator.addActionsTo(&menu); QList urls; urls << QUrl::fromLocalFile(m_srcFile); generator.setUrls(urls); - QCOMPARE(extractActionNames(menu), QStringList() << "copyTo_submenu" << "moveTo_submenu"); + QCOMPARE(extractActionNames(menu), QStringList() << QStringLiteral("copyTo_submenu") << QStringLiteral("moveTo_submenu")); //menu.popup(QPoint(-50, -50)); QMenu *copyMenu = menu.actions().at(0)->menu(); // "copy" submenu QVERIFY(copyMenu); // When copyMenu->popup(QPoint(-100, -100)); // Then const QStringList actionNames = extractActionNames(*copyMenu); - QCOMPARE(actionNames.first(), QString("home")); - QVERIFY(actionNames.contains("browse")); + QCOMPARE(actionNames.first(), QStringLiteral("home")); + QVERIFY(actionNames.contains(QStringLiteral("browse"))); QCOMPARE(actionNames.at(actionNames.count() - 2), m_nonWritableTempDir.path()); QCOMPARE(actionNames.last(), m_destDir); } void shouldTryCopyingToRecentPath_data() { QTest::addColumn("actionNumber"); // from the bottom of the menu, starting at 1; see the recentDirs list in initTestCase QTest::addColumn("expectedErrorCode"); QTest::newRow("working") << 1 << 0; // no error QTest::newRow("non_writable") << 2 << int(KIO::ERR_WRITE_ACCESS_DENIED); QTest::newRow("non_existing") << 3 << int(KIO::ERR_CANNOT_OPEN_FOR_WRITING); } void shouldTryCopyingToRecentPath() { QFETCH(int, actionNumber); QFETCH(int, expectedErrorCode); KFileCopyToMenu generator(&m_parentWidget); QMenu menu; QList urls; urls << QUrl::fromLocalFile(m_srcFile); generator.setUrls(urls); generator.addActionsTo(&menu); QMenu *copyMenu = menu.actions().at(0)->menu(); copyMenu->popup(QPoint(-100, -100)); const QList actions = copyMenu->actions(); if (m_lastActionCount == 0) { m_lastActionCount = actions.count(); } else { QCOMPARE(actions.count(), m_lastActionCount); // should be stable, i.e. selecting a recent dir shouldn't duplicate it } QAction *copyAction = actions.at(actions.count() - actionNumber); QSignalSpy spy(&generator, SIGNAL(error(int,QString))); // When copyAction->trigger(); // Then QTRY_COMPARE(spy.count(), expectedErrorCode ? 1 : 0); if (expectedErrorCode) { QCOMPARE(spy.at(0).at(0).toInt(), expectedErrorCode); } else { - QTRY_VERIFY(QFile::exists(m_destDir + "/srcfile")); + QTRY_VERIFY(QFile::exists(m_destDir + QStringLiteral("/srcfile"))); } } private: static QStringList extractActionNames(const QMenu &menu) { QStringList ret; foreach (const QAction *action, menu.actions()) { ret.append(action->objectName()); } return ret; } QTemporaryDir m_tempDir; QString m_srcDir; QString m_srcFile; QTemporaryDir m_tempDestDir; QString m_destDir; QTemporaryDir m_nonWritableTempDir; QWidget m_parentWidget; QStringList m_recentDirs; int m_lastActionCount; }; QTEST_MAIN(KFileCopyToMenuTest) #include "kfilecopytomenutest.moc" diff --git a/autotests/kiotesthelper.h b/autotests/kiotesthelper.h index 2530f1b5..3663a512 100644 --- a/autotests/kiotesthelper.h +++ b/autotests/kiotesthelper.h @@ -1,205 +1,205 @@ /* This file is part of the KDE project Copyright (C) 2006 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // This file can only be included once in a given binary #include #include #include #include #include #ifdef Q_OS_UNIX #include #else #include #endif #include #include "kioglobal_p.h" QString homeTmpDir() { - const QString dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/kiotests/"); + const QString dir(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QStringLiteral("/kiotests/")); if (!QFile::exists(dir)) { const bool ok = QDir().mkpath(dir); if (!ok) { qFatal("Couldn't create %s", qPrintable(dir)); } } return dir; } static QDateTime s_referenceTimeStamp; static void setTimeStamp(const QString &path, const QDateTime &mtime) { #ifdef Q_OS_UNIX // Put timestamp in the past so that we can check that the listing is correct struct utimbuf utbuf; utbuf.actime = mtime.toTime_t(); utbuf.modtime = utbuf.actime; utime(QFile::encodeName(path), &utbuf); //qDebug( "Time changed for %s", qPrintable( path ) ); #elif defined(Q_OS_WIN) struct _utimbuf utbuf; utbuf.actime = mtime.toTime_t(); utbuf.modtime = utbuf.actime; _wutime(reinterpret_cast(path.utf16()), &utbuf); #endif } static void createTestFile(const QString &path, bool plainText = false) { QDir().mkpath(QFileInfo(path).absolutePath()); QFile f(path); if (!f.open(QIODevice::WriteOnly)) { qFatal("Couldn't create %s", qPrintable(path)); } QByteArray data(plainText ? "Hello world" : "Hello\0world", 11); QCOMPARE(data.size(), 11); f.write(data); f.close(); setTimeStamp(path, s_referenceTimeStamp); } static void createTestSymlink(const QString &path, const QByteArray &target = "/IDontExist") { QFile::remove(path); - bool ok = KIOPrivate::createSymlink(target, path); // broken symlink + bool ok = KIOPrivate::createSymlink(QString::fromLatin1(target), path); // broken symlink if (!ok) { qFatal("couldn't create symlink: %s", strerror(errno)); } QT_STATBUF buf; QVERIFY(QT_LSTAT(QFile::encodeName(path), &buf) == 0); QVERIFY((buf.st_mode & QT_STAT_MASK) == QT_STAT_LNK); //qDebug( "symlink %s created", qPrintable( path ) ); QVERIFY(QFileInfo(path).isSymLink()); } enum CreateTestDirectoryOptions { DefaultOptions = 0, NoSymlink = 1 }; static inline void createTestDirectory(const QString &path, CreateTestDirectoryOptions opt = DefaultOptions) { QDir dir; bool ok = dir.mkdir(path); if (!ok && !dir.exists()) { qFatal("Couldn't create %s", qPrintable(path)); } - createTestFile(path + "/testfile"); + createTestFile(path + QStringLiteral("/testfile")); if ((opt & NoSymlink) == 0) { #ifndef Q_OS_WIN - createTestSymlink(path + "/testlink"); - QVERIFY(QFileInfo(path + "/testlink").isSymLink()); + createTestSymlink(path + QStringLiteral("/testlink")); + QVERIFY(QFileInfo(path + QStringLiteral("/testlink")).isSymLink()); #else // to not change the filecount everywhere in the tests - createTestFile(path + "/testlink"); + createTestFile(path + QStringLiteral("/testlink")); #endif } setTimeStamp(path, s_referenceTimeStamp); } #include class PredefinedAnswerJobUiDelegate : public KIO::JobUiDelegateExtension { public: PredefinedAnswerJobUiDelegate() : JobUiDelegateExtension(), m_askFileRenameCalled(0), m_askSkipCalled(0), m_askDeleteCalled(0), m_messageBoxCalled(0), m_renameResult(KIO::R_SKIP), m_skipResult(KIO::S_SKIP), m_deleteResult(false), m_messageBoxResult(0) { } KIO::RenameDialog_Result askFileRename(KJob *job, const QString &caption, const QUrl &src, const QUrl &dest, KIO::RenameDialog_Options options, QString &newDest, KIO::filesize_t = (KIO::filesize_t) - 1, KIO::filesize_t = (KIO::filesize_t) - 1, const QDateTime & = QDateTime(), const QDateTime & = QDateTime(), const QDateTime & = QDateTime(), const QDateTime & = QDateTime()) override { Q_UNUSED(job) Q_UNUSED(caption) Q_UNUSED(src) Q_UNUSED(dest) Q_UNUSED(options) Q_UNUSED(newDest) ++m_askFileRenameCalled; return m_renameResult; } KIO::SkipDialog_Result askSkip(KJob *job, KIO::SkipDialog_Options options, const QString &error_text) override { Q_UNUSED(job) Q_UNUSED(options) Q_UNUSED(error_text) ++m_askSkipCalled; return m_skipResult; } bool askDeleteConfirmation(const QList &urls, DeletionType deletionType, ConfirmationType confirmationType) override { Q_UNUSED(urls); Q_UNUSED(deletionType); Q_UNUSED(confirmationType); ++m_askDeleteCalled; return m_deleteResult; } int requestMessageBox(MessageBoxType type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo, const QString &iconYes = QString(), const QString &iconNo = QString(), const QString &dontAskAgainName = QString(), const KIO::MetaData &sslMetaData = KIO::MetaData()) override { Q_UNUSED(type); Q_UNUSED(text); Q_UNUSED(caption); Q_UNUSED(buttonYes); Q_UNUSED(buttonNo); Q_UNUSED(iconYes); Q_UNUSED(iconNo); Q_UNUSED(dontAskAgainName); Q_UNUSED(sslMetaData); ++m_messageBoxCalled; return m_messageBoxResult; } // yeah, public, for get and reset. int m_askFileRenameCalled; int m_askSkipCalled; int m_askDeleteCalled; int m_messageBoxCalled; KIO::RenameDialog_Result m_renameResult; KIO::SkipDialog_Result m_skipResult; bool m_deleteResult; int m_messageBoxResult; }; diff --git a/autotests/klocalsocketservertest.cpp b/autotests/klocalsocketservertest.cpp index ab093fdc..6e76ef53 100644 --- a/autotests/klocalsocketservertest.cpp +++ b/autotests/klocalsocketservertest.cpp @@ -1,311 +1,311 @@ /* * This file is part of the KDE libraries * Copyright (C) 2007 Thiago Macieira * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "klocalsocketservertest.h" #include #include #include #include #include "klocalsocket.h" static const char afile[] = "/tmp/afile"; static const char asocket[] = "/tmp/asocket"; tst_KLocalSocketServer::tst_KLocalSocketServer() { - QFile f(QFile::encodeName(afile)); + QFile f(QString::fromLatin1(QFile::encodeName(QLatin1String(afile)))); f.open(QIODevice::ReadWrite | QIODevice::Truncate); } tst_KLocalSocketServer::~tst_KLocalSocketServer() { - QFile::remove(afile); + QFile::remove(QLatin1String(afile)); } class TimedConnection: public QThread { Q_OBJECT public: - ~TimedConnection() + ~TimedConnection() override { wait(); } protected: void run() override { KLocalSocket socket; QThread::usleep(200); - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); socket.waitForConnected(); } }; void tst_KLocalSocketServer::cleanup() { - QFile::remove(asocket); + QFile::remove(QLatin1String(asocket)); } void tst_KLocalSocketServer::listen_data() { QTest::addColumn("path"); QTest::addColumn("success"); QTest::newRow("null") << QString() << false; QTest::newRow("empty") << "" << false; QTest::newRow("a-dir") << "/tmp/" << false; QTest::newRow("not-a-dir") << QString(afile + QLatin1String("/foo")) << false; QTest::newRow("not-permitted") << "/root/foo" << false; QTest::newRow("valid") << asocket << true; } void tst_KLocalSocketServer::listen() { QFETCH(QString, path); KLocalSocketServer server; QTEST(server.listen(path), "success"); } void tst_KLocalSocketServer::waitForConnection() { KLocalSocketServer server; - QVERIFY(server.listen(asocket)); + QVERIFY(server.listen(QLatin1String(asocket))); QVERIFY(!server.hasPendingConnections()); { KLocalSocket socket; - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); QVERIFY(socket.waitForConnected()); // make sure we can accept that connection QVERIFY(server.waitForNewConnection()); QVERIFY(server.hasPendingConnections()); delete server.nextPendingConnection(); } // test a timeout now QVERIFY(!server.hasPendingConnections()); QVERIFY(!server.waitForNewConnection(0)); QVERIFY(!server.waitForNewConnection(200)); { // now try a timed connection TimedConnection conn; conn.start(); QVERIFY(server.waitForNewConnection(500)); QVERIFY(server.hasPendingConnections()); delete server.nextPendingConnection(); } } void tst_KLocalSocketServer::newConnection() { KLocalSocketServer server; - QVERIFY(server.listen(asocket)); + QVERIFY(server.listen(QLatin1String(asocket))); QVERIFY(!server.hasPendingConnections()); // catch the signal QSignalSpy spy(&server, SIGNAL(newConnection())); KLocalSocket socket; - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); QVERIFY(socket.waitForConnected()); // let the events be processed QTest::qWait(100); QVERIFY(spy.count() == 1); } void tst_KLocalSocketServer::accept() { KLocalSocketServer server; - QVERIFY(server.listen(asocket)); + QVERIFY(server.listen(QLatin1String(asocket))); QVERIFY(!server.hasPendingConnections()); KLocalSocket socket; - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); QVERIFY(socket.waitForConnected()); QVERIFY(server.waitForNewConnection()); QVERIFY(server.hasPendingConnections()); KLocalSocket *socket2 = server.nextPendingConnection(); QVERIFY(!server.hasPendingConnections()); QCOMPARE(socket.state(), QAbstractSocket::ConnectedState); QCOMPARE(socket2->state(), QAbstractSocket::ConnectedState); delete socket2; } void tst_KLocalSocketServer::state() { KLocalSocketServer server; // sanity check of the initial state: QVERIFY(!server.isListening()); QVERIFY(server.localPath().isEmpty()); QCOMPARE(int(server.localSocketType()), int(KLocalSocket::UnknownLocalSocketType)); QVERIFY(!server.hasPendingConnections()); QVERIFY(!server.nextPendingConnection()); // it's not connected, so it shouldn't change timedOut bool timedOut = true; QVERIFY(!server.waitForNewConnection(0, &timedOut)); QVERIFY(timedOut); timedOut = false; QVERIFY(!server.waitForNewConnection(0, &timedOut)); QVERIFY(!timedOut); // start listening: - QVERIFY(server.listen(asocket)); + QVERIFY(server.listen(QLatin1String(asocket))); QVERIFY(server.isListening()); - QCOMPARE(server.localPath(), QString(asocket)); + QCOMPARE(server.localPath(), QString(QLatin1String(asocket))); QCOMPARE(int(server.localSocketType()), int(KLocalSocket::UnixSocket)); QVERIFY(!server.hasPendingConnections()); QVERIFY(!server.nextPendingConnection()); // it must timeout now: timedOut = false; QVERIFY(!server.waitForNewConnection(0, &timedOut)); QVERIFY(timedOut); // make a connection: KLocalSocket socket; - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); QVERIFY(socket.waitForConnected()); // it mustn't time out now: timedOut = true; QVERIFY(server.waitForNewConnection(0, &timedOut)); QVERIFY(!timedOut); QVERIFY(server.hasPendingConnections()); KLocalSocket *socket2 = server.nextPendingConnection(); QVERIFY(socket2); delete socket2; // close: server.close(); // verify state: QVERIFY(!server.isListening()); QVERIFY(server.localPath().isEmpty()); QCOMPARE(int(server.localSocketType()), int(KLocalSocket::UnknownLocalSocketType)); QVERIFY(!server.hasPendingConnections()); QVERIFY(!server.nextPendingConnection()); } void tst_KLocalSocketServer::setMaxPendingConnections() { KLocalSocketServer server; - QVERIFY(server.listen(asocket)); + QVERIFY(server.listen(QLatin1String(asocket))); QVERIFY(!server.hasPendingConnections()); server.setMaxPendingConnections(0); // we don't want to receive // check if the event loop won't cause a connection to accepted KLocalSocket socket; - socket.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); QTest::qWait(100); // 100 ms doing absolutely nothing QVERIFY(!server.hasPendingConnections()); // now check if we get that connection server.setMaxPendingConnections(1); QTest::qWait(100); QVERIFY(server.hasPendingConnections()); delete server.nextPendingConnection(); QVERIFY(socket.waitForDisconnected()); // check if we receive only one of the two pending connections KLocalSocket socket2; - socket.connectToPath(asocket); - socket2.connectToPath(asocket); + socket.connectToPath(QLatin1String(asocket)); + socket2.connectToPath(QLatin1String(asocket)); QTest::qWait(100); QVERIFY(server.hasPendingConnections()); delete server.nextPendingConnection(); QVERIFY(!server.hasPendingConnections()); QVERIFY(!server.nextPendingConnection()); } void tst_KLocalSocketServer::abstractUnixSocket_data() { #ifndef Q_OS_LINUX QSKIP("Abstract UNIX sockets are specific for Linux"); #endif QTest::addColumn("path"); QTest::addColumn("success"); QTest::newRow("null") << QString() << false; QTest::newRow("empty") << "" << false; #if 0 // apparently, we are allowed to put sockets there, even if we don't have permission to QTest::newRow("a-dir") << "/tmp/" << false; QTest::newRow("not-a-dir") << afile + QLatin1String("/foo") << false; QTest::newRow("not-permitted") << "/root/foo" << false; #endif QTest::newRow("valid") << asocket << true; } void tst_KLocalSocketServer::abstractUnixSocket() { QFETCH(QString, path); QFETCH(bool, success); if (success) { QVERIFY(!QFile::exists(path)); } KLocalSocketServer server; QCOMPARE(server.listen(path, KLocalSocket::AbstractUnixSocket), success); if (success) { // the socket must not exist in the filesystem QVERIFY(!QFile::exists(path)); // now try to connect to it KLocalSocket socket; socket.connectToPath(path, KLocalSocket::AbstractUnixSocket); QVERIFY(socket.waitForConnected(100)); QVERIFY(server.waitForNewConnection(100)); QVERIFY(server.hasPendingConnections()); // the socket must still not exist in the filesystem QVERIFY(!QFile::exists(path)); // verify that they can exchange data too: KLocalSocket *socket2 = server.nextPendingConnection(); QByteArray data("Hello"); socket2->write(data); QVERIFY(socket2->bytesToWrite() == 0 || socket2->waitForBytesWritten(100)); QVERIFY(socket.waitForReadyRead(100)); QCOMPARE(socket.read(data.length()), data); socket.write(data); QVERIFY(socket.bytesToWrite() == 0 || socket.waitForBytesWritten(100)); QVERIFY(socket2->waitForReadyRead(100)); QCOMPARE(socket2->read(data.length()), data); delete socket2; QVERIFY(socket.waitForDisconnected(100)); } } QTEST_MAIN(tst_KLocalSocketServer) #include "klocalsocketservertest.moc" diff --git a/autotests/knewfilemenutest.cpp b/autotests/knewfilemenutest.cpp index a71acc8b..d0bc57c0 100644 --- a/autotests/knewfilemenutest.cpp +++ b/autotests/knewfilemenutest.cpp @@ -1,194 +1,194 @@ /* This file is part of the KDE libraries Copyright (c) 2012 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include #include #endif class KNewFileMenuTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { qputenv("KDE_FORK_SLAVES", "yes"); // to avoid a runtime dependency on klauncher #ifdef Q_OS_UNIX m_umask = ::umask(0); ::umask(m_umask); #endif QVERIFY(m_tmpDir.isValid()); } void cleanupTestCase() { } // Ensure that we can use storedPut() with a qrc file as input // similar to JobTest::storedPutIODeviceFile, but with a qrc file as input // (and here because jobtest doesn't link to KIO::FileWidgets, which has the qrc) void storedPutIODeviceQrcFile() { // Given a source (in a Qt resource file) and a destination file - const QString src = ":/kio5/newfile-templates/.source/HTMLFile.html"; + const QString src = QStringLiteral(":/kio5/newfile-templates/.source/HTMLFile.html"); QVERIFY(QFile::exists(src)); QFile srcFile(src); QVERIFY(srcFile.open(QIODevice::ReadOnly)); - const QString dest = m_tmpDir.path() + "/dest"; + const QString dest = m_tmpDir.path() + QStringLiteral("/dest"); QFile::remove(dest); const QUrl destUrl = QUrl::fromLocalFile(dest); // When using storedPut with the QFile as argument KIO::StoredTransferJob *job = KIO::storedPut(&srcFile, destUrl, -1, KIO::Overwrite | KIO::HideProgressInfo); // Then the copy should succeed and the dest file exist QVERIFY2(job->exec(), qPrintable(job->errorString())); QVERIFY(QFile::exists(dest)); QCOMPARE(QFileInfo(src).size(), QFileInfo(dest).size()); // And the permissions should respect the umask (#359581) #ifdef Q_OS_UNIX if (m_umask & S_IWOTH) { QVERIFY2(!(QFileInfo(dest).permissions() & QFileDevice::WriteOther), qPrintable(dest)); } if (m_umask & S_IWGRP) { QVERIFY(!(QFileInfo(dest).permissions() & QFileDevice::WriteGroup)); } #endif QFile::remove(dest); } void test_data() { QTest::addColumn("actionText"); // the action we're clicking on QTest::addColumn("expectedDefaultFilename"); // the initial filename in the dialog QTest::addColumn("typedFilename"); // what the user is typing QTest::addColumn("expectedFilename"); // the final file name QTest::newRow("text file") << "Text File" << "Text File" << "tmp_knewfilemenutest.txt" << "tmp_knewfilemenutest.txt"; QTest::newRow("text file with jpeg extension") << "Text File" << "Text File" << "foo.jpg" << "foo.jpg.txt"; QTest::newRow("html file") << "HTML File" << "HTML File" << "foo.html" << "foo.html"; QTest::newRow("url desktop file") << "Link to Location " << "" << "tmp_link.desktop" << "tmp_link.desktop"; QTest::newRow("url desktop file no extension") << "Link to Location " << "" << "tmp_link" << "tmp_link"; QTest::newRow("url desktop file .pl extension") << "Link to Location " << "" << "tmp_link.pl" << "tmp_link.pl.desktop"; QTest::newRow("symlink") << "Basic link" << "" << "thelink" << "thelink"; QTest::newRow("folder") << "Folder..." << "New Folder" << "folder1" << "folder1"; QTest::newRow("folder_default_name") << "Folder..." << "New Folder" << "New Folder" << "New Folder"; QTest::newRow("folder_with_suggested_name") << "Folder..." << "New Folder (1)" << "New Folder" << "New Folder"; QTest::newRow("application") << "Link to Application..." << "Link to Application" << "app1" << "app1.desktop"; } void test() { QFETCH(QString, actionText); QFETCH(QString, expectedDefaultFilename); QFETCH(QString, typedFilename); QFETCH(QString, expectedFilename); QWidget parentWidget; KActionCollection coll(this, QStringLiteral("foo")); KNewFileMenu menu(&coll, QStringLiteral("the_action"), this); menu.setModal(false); menu.setParentWidget(&parentWidget); QList lst; lst << QUrl::fromLocalFile(m_tmpDir.path()); menu.setPopupFiles(lst); menu.checkUpToDate(); QAction *action = coll.action(QStringLiteral("the_action")); QVERIFY(action); QAction *textAct = nullptr; Q_FOREACH (QAction *act, action->menu()->actions()) { if (act->text().contains(actionText)) { textAct = act; } } if (!textAct) { Q_FOREACH (QAction *act, action->menu()->actions()) { qDebug() << act << act->text() << act->data(); } - const QString err = "action with text \"" + actionText + "\" not found."; + const QString err = QStringLiteral("action with text \"") + actionText + QStringLiteral("\" not found."); QVERIFY2(textAct, qPrintable(err)); } textAct->trigger(); QDialog *dialog = parentWidget.findChild(); QVERIFY(dialog); if (KNameAndUrlInputDialog *nauiDialog = qobject_cast(dialog)) { QCOMPARE(nauiDialog->name(), expectedDefaultFilename); nauiDialog->setSuggestedName(typedFilename); nauiDialog->setSuggestedUrl(QUrl(QStringLiteral("file:///etc"))); } else if (KPropertiesDialog *propsDialog = qobject_cast(dialog)) { - QLineEdit *lineEdit = propsDialog->findChild("KFilePropsPlugin::nameLineEdit"); + QLineEdit *lineEdit = propsDialog->findChild(QStringLiteral("KFilePropsPlugin::nameLineEdit")); QVERIFY(lineEdit); QCOMPARE(lineEdit->text(), expectedDefaultFilename); lineEdit->setText(typedFilename); } else { QLineEdit *lineEdit = dialog->findChild(); QVERIFY(lineEdit); QCOMPARE(lineEdit->text(), expectedDefaultFilename); lineEdit->setText(typedFilename); } QUrl emittedUrl; QSignalSpy spy(&menu, SIGNAL(fileCreated(QUrl))); QSignalSpy folderSpy(&menu, SIGNAL(directoryCreated(QUrl))); dialog->accept(); - const QString path = m_tmpDir.path() + '/' + expectedFilename; + const QString path = m_tmpDir.path() + QLatin1Char('/') + expectedFilename; if (actionText == QLatin1String("Folder...")) { QVERIFY(folderSpy.wait(1000)); emittedUrl = folderSpy.at(0).at(0).toUrl(); QVERIFY(QFileInfo(path).isDir()); } else { if (spy.isEmpty()) { QVERIFY(spy.wait(1000)); } emittedUrl = spy.at(0).at(0).toUrl(); QVERIFY(QFile::exists(path)); if (actionText != QLatin1String("Basic link")) { QFile file(path); QVERIFY(file.open(QIODevice::ReadOnly)); const QByteArray contents = file.readAll(); if (actionText.startsWith(QLatin1String("HTML"))) { QCOMPARE(QString::fromLatin1(contents.left(6)), QStringLiteral("")); } } } QCOMPARE(emittedUrl.toLocalFile(), path); } private: QTemporaryDir m_tmpDir; #ifdef Q_OS_UNIX mode_t m_umask; #endif }; QTEST_MAIN(KNewFileMenuTest) #include "knewfilemenutest.moc" diff --git a/autotests/kurlcompletiontest.cpp b/autotests/kurlcompletiontest.cpp index 16e89f56..511667ac 100644 --- a/autotests/kurlcompletiontest.cpp +++ b/autotests/kurlcompletiontest.cpp @@ -1,446 +1,446 @@ /* * Copyright (C) 2004 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include class KUrlCompletionTest : public QObject { Q_OBJECT private Q_SLOTS: void test(); public: KUrlCompletionTest() { #ifdef NO_WAIT // kurlcompletiontest-nowait sets this, to test what happens on slower systems (or systems with many dirs or users) qputenv("KURLCOMPLETION_WAIT", "1"); // 1ms, too short for a full listing of /usr/bin, but at least give a chance for a few items in the result #endif } ~KUrlCompletionTest() { teardown(); } void runAllTests(); void setup(); void teardown(); void testLocalRelativePath(); void testLocalAbsolutePath(); void testLocalURL(); void testEmptyCwd(); void testBug346920(); void testInvalidProtocol(); void testUser(); void testCancel(); // remember to register new test methods in runAllTests private: void waitForCompletion(KUrlCompletion *completion); KUrlCompletion *m_completion; KUrlCompletion *m_completionWithMimeFilter; QTemporaryDir *m_tempDir; QUrl m_dirURL; QString m_dir; KUrlCompletion *m_completionEmptyCwd; }; void KUrlCompletionTest::setup() { qDebug(); m_completion = new KUrlCompletion; m_completionWithMimeFilter = new KUrlCompletion; m_completionWithMimeFilter->setMimeTypeFilters({QStringLiteral("text/x-c++src")}); m_tempDir = new QTemporaryDir; m_dir = m_tempDir->path(); m_dir += QLatin1String("/Dir With#Spaces/"); QDir().mkdir(m_dir); qDebug() << "m_dir=" << m_dir; m_completion->setDir(QUrl::fromLocalFile(m_dir)); m_completionWithMimeFilter->setDir(m_completion->dir()); m_dirURL = QUrl::fromLocalFile(m_dir); - QFile f1(m_dir + "/file1"); + QFile f1(m_dir + QStringLiteral("/file1")); bool ok = f1.open(QIODevice::WriteOnly); QVERIFY(ok); f1.close(); - QFile f2(m_dir + "/file#a"); + QFile f2(m_dir + QStringLiteral("/file#a")); ok = f2.open(QIODevice::WriteOnly); QVERIFY(ok); f2.close(); - QFile f3(m_dir + "/file."); + QFile f3(m_dir + QStringLiteral("/file.")); ok = f3.open(QIODevice::WriteOnly); QVERIFY(ok); f3.close(); - QFile f4(m_dir + "/source.cpp"); + QFile f4(m_dir + QStringLiteral("/source.cpp")); ok = f4.open(QIODevice::WriteOnly); QVERIFY(ok); f4.close(); - QFile f5(m_dir + "/source.php"); + QFile f5(m_dir + QStringLiteral("/source.php")); ok = f5.open(QIODevice::WriteOnly); QVERIFY(ok); f5.close(); - QDir().mkdir(m_dir + "/file_subdir"); - QDir().mkdir(m_dir + "/.1_hidden_file_subdir"); - QDir().mkdir(m_dir + "/.2_hidden_file_subdir"); + QDir().mkdir(m_dir + QStringLiteral("/file_subdir")); + QDir().mkdir(m_dir + QStringLiteral("/.1_hidden_file_subdir")); + QDir().mkdir(m_dir + QStringLiteral("/.2_hidden_file_subdir")); m_completionEmptyCwd = new KUrlCompletion; m_completionEmptyCwd->setDir(QUrl()); } void KUrlCompletionTest::teardown() { delete m_completion; m_completion = nullptr; delete m_completionWithMimeFilter; m_completionWithMimeFilter = nullptr; delete m_tempDir; m_tempDir = nullptr; delete m_completionEmptyCwd; m_completionEmptyCwd = nullptr; } void KUrlCompletionTest::waitForCompletion(KUrlCompletion *completion) { while (completion->isRunning()) { qDebug() << "waiting for thread..."; QTest::qWait(5); } // The thread emitted a signal, process it. qApp->sendPostedEvents(nullptr, QEvent::MetaCall); } void KUrlCompletionTest::testLocalRelativePath() { qDebug(); // Completion from relative path, with two matches m_completion->makeCompletion(QStringLiteral("f")); waitForCompletion(m_completion); QStringList comp1all = m_completion->allMatches(); qDebug() << comp1all; QCOMPARE(comp1all.count(), 4); - QVERIFY(comp1all.contains("file1")); - QVERIFY(comp1all.contains("file#a")); - QVERIFY(comp1all.contains("file.")); - QVERIFY(comp1all.contains("file_subdir/")); + QVERIFY(comp1all.contains(QStringLiteral("file1"))); + QVERIFY(comp1all.contains(QStringLiteral("file#a"))); + QVERIFY(comp1all.contains(QStringLiteral("file."))); + QVERIFY(comp1all.contains(QStringLiteral("file_subdir/"))); QString comp1 = m_completion->replacedPath(QStringLiteral("file1")); // like KUrlRequester does - QCOMPARE(comp1, QString("file1")); + QCOMPARE(comp1, QStringLiteral("file1")); // Completion from relative path qDebug() << endl << "now completing on 'file#'"; m_completion->makeCompletion(QStringLiteral("file#")); QVERIFY(!m_completion->isRunning()); // last listing reused QStringList compall = m_completion->allMatches(); qDebug() << compall; QCOMPARE(compall.count(), 1); - QCOMPARE(compall.first(), QString("file#a")); + QCOMPARE(compall.first(), QStringLiteral("file#a")); QString comp2 = m_completion->replacedPath(compall.first()); // like KUrlRequester does - QCOMPARE(comp2, QString("file#a")); + QCOMPARE(comp2, QStringLiteral("file#a")); // Completion with empty string qDebug() << endl << "now completing on ''"; m_completion->makeCompletion(QLatin1String("")); waitForCompletion(m_completion); QStringList compEmpty = m_completion->allMatches(); QCOMPARE(compEmpty.count(), 0); - m_completion->makeCompletion("."); + m_completion->makeCompletion(QStringLiteral(".")); waitForCompletion(m_completion); const auto compAllHidden = m_completion->allMatches(); QCOMPARE(compAllHidden.count(), 2); - QVERIFY(compAllHidden.contains(".1_hidden_file_subdir/")); - QVERIFY(compAllHidden.contains(".2_hidden_file_subdir/")); + QVERIFY(compAllHidden.contains(QStringLiteral(".1_hidden_file_subdir/"))); + QVERIFY(compAllHidden.contains(QStringLiteral(".2_hidden_file_subdir/"))); // Completion with '.2', should find only hidden folders starting with '2' - m_completion->makeCompletion(".2"); + m_completion->makeCompletion(QStringLiteral(".2")); waitForCompletion(m_completion); const auto compHiddenStartingWith2 = m_completion->allMatches(); QCOMPARE(compHiddenStartingWith2.count(), 1); - QVERIFY(compHiddenStartingWith2.contains(".2_hidden_file_subdir/")); + QVERIFY(compHiddenStartingWith2.contains(QStringLiteral(".2_hidden_file_subdir/"))); // Completion with 'file.', should only find one file - m_completion->makeCompletion("file."); + m_completion->makeCompletion(QStringLiteral("file.")); waitForCompletion(m_completion); const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); - QVERIFY(compFileEndingWithDot.contains("file.")); + QVERIFY(compFileEndingWithDot.contains(QStringLiteral("file."))); // Completion with 'source' should only find the C++ file - m_completionWithMimeFilter->makeCompletion("source"); + m_completionWithMimeFilter->makeCompletion(QStringLiteral("source")); waitForCompletion(m_completionWithMimeFilter); const auto compSourceFile = m_completionWithMimeFilter->allMatches(); QCOMPARE(compSourceFile.count(), 1); - QVERIFY(compSourceFile.contains("source.cpp")); + QVERIFY(compSourceFile.contains(QStringLiteral("source.cpp"))); // But it should also be able to find folders - m_completionWithMimeFilter->makeCompletion("file_subdir"); + m_completionWithMimeFilter->makeCompletion(QStringLiteral("file_subdir")); waitForCompletion(m_completionWithMimeFilter); const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); QCOMPARE(compMimeFolder.count(), 1); - QVERIFY(compMimeFolder.contains("file_subdir/")); + QVERIFY(compMimeFolder.contains(QStringLiteral("file_subdir/"))); } void KUrlCompletionTest::testLocalAbsolutePath() { // Completion from absolute path qDebug() << m_dir + "file#"; m_completion->makeCompletion(m_dir + "file#"); waitForCompletion(m_completion); QStringList compall = m_completion->allMatches(); qDebug() << compall; QCOMPARE(compall.count(), 1); QString comp = compall.first(); QCOMPARE(comp, QString(m_dir + "file#a")); comp = m_completion->replacedPath(comp); // like KUrlRequester does QCOMPARE(comp, QString(m_dir + "file#a")); // Completion with '.', should find all hidden folders m_completion->makeCompletion(m_dir + "."); waitForCompletion(m_completion); const auto compAllHidden = m_completion->allMatches(); QCOMPARE(compAllHidden.count(), 2); QVERIFY(compAllHidden.contains(m_dir + ".1_hidden_file_subdir/")); QVERIFY(compAllHidden.contains(m_dir + ".2_hidden_file_subdir/")); // Completion with '.2', should find only hidden folders starting with '2' m_completion->makeCompletion(m_dir + ".2"); waitForCompletion(m_completion); const auto compHiddenStartingWith2 = m_completion->allMatches(); QCOMPARE(compHiddenStartingWith2.count(), 1); QVERIFY(compHiddenStartingWith2.contains(m_dir + ".2_hidden_file_subdir/")); // Completion with 'file.', should only find one file m_completion->makeCompletion(m_dir + "file."); waitForCompletion(m_completion); const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); QVERIFY(compFileEndingWithDot.contains(m_dir + "file.")); // Completion with 'source' should only find the C++ file m_completionWithMimeFilter->makeCompletion(m_dir + "source"); waitForCompletion(m_completionWithMimeFilter); const auto compSourceFile = m_completionWithMimeFilter->allMatches(); QCOMPARE(compSourceFile.count(), 1); QVERIFY(compSourceFile.contains(m_dir + "source.cpp")); // But it should also be able to find folders m_completionWithMimeFilter->makeCompletion(m_dir + "file_subdir"); waitForCompletion(m_completionWithMimeFilter); const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); QCOMPARE(compMimeFolder.count(), 1); QVERIFY(compMimeFolder.contains(m_dir + "file_subdir/")); } void KUrlCompletionTest::testLocalURL() { // Completion from URL qDebug(); QUrl url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + "file"); m_completion->makeCompletion(url.toString()); waitForCompletion(m_completion); QStringList comp1all = m_completion->allMatches(); qDebug() << comp1all; QCOMPARE(comp1all.count(), 4); qDebug() << "Looking for" << m_dirURL.toString() + "file1"; QVERIFY(comp1all.contains(m_dirURL.toString() + "file1")); qDebug() << "Looking for" << m_dirURL.toString() + "file."; QVERIFY(comp1all.contains(m_dirURL.toString() + "file.")); QVERIFY(comp1all.contains(m_dirURL.toString() + "file_subdir/")); QString filehash = m_dirURL.toString() + "file%23a"; qDebug() << "Looking for" << filehash; QVERIFY(comp1all.contains(filehash)); QString filehashPath = m_completion->replacedPath(filehash); // note that it returns a path!! qDebug() << filehashPath; QCOMPARE(filehashPath, QString(m_dirURL.toLocalFile() + "file#a")); // Completion from URL with no match url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + "foobar"); qDebug() << "makeCompletion(" << url << ")"; QString comp2 = m_completion->makeCompletion(url.toString()); QVERIFY(comp2.isEmpty()); waitForCompletion(m_completion); QVERIFY(m_completion->allMatches().isEmpty()); // Completion from URL with a ref -> no match url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + 'f'); url.setFragment(QStringLiteral("ref")); qDebug() << "makeCompletion(" << url << ")"; m_completion->makeCompletion(url.toString()); waitForCompletion(m_completion); QVERIFY(m_completion->allMatches().isEmpty()); // Completion with '.', should find all hidden folders qDebug() << "makeCompletion(" << (m_dirURL.toString() + ".") << ")"; m_completion->makeCompletion(m_dirURL.toString() + "."); waitForCompletion(m_completion); const auto compAllHidden = m_completion->allMatches(); QCOMPARE(compAllHidden.count(), 2); QVERIFY(compAllHidden.contains(m_dirURL.toString() + ".1_hidden_file_subdir/")); QVERIFY(compAllHidden.contains(m_dirURL.toString() + ".2_hidden_file_subdir/")); // Completion with '.2', should find only hidden folders starting with '2' url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + ".2"); qDebug() << "makeCompletion(" << url << ")"; m_completion->makeCompletion(url.toString()); waitForCompletion(m_completion); const auto compHiddenStartingWith2 = m_completion->allMatches(); QCOMPARE(compHiddenStartingWith2.count(), 1); - QVERIFY(compHiddenStartingWith2.contains(m_dirURL.toString() + ".2_hidden_file_subdir/")); + QVERIFY(compHiddenStartingWith2.contains(m_dirURL.toString() + QStringLiteral(".2_hidden_file_subdir/"))); // Completion with 'file.', should only find one file - url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + "file."); + url = QUrl::fromLocalFile(m_dirURL.toLocalFile() + QStringLiteral("file.")); qDebug() << "makeCompletion(" << url << ")"; m_completion->makeCompletion(url.toString()); waitForCompletion(m_completion); const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); - QVERIFY(compFileEndingWithDot.contains(m_dirURL.toString() + "file.")); + QVERIFY(compFileEndingWithDot.contains(m_dirURL.toString() + QStringLiteral("file."))); // Completion with 'source' should only find the C++ file - m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + "source"); + m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + QStringLiteral("source")); waitForCompletion(m_completionWithMimeFilter); const auto compSourceFile = m_completionWithMimeFilter->allMatches(); QCOMPARE(compSourceFile.count(), 1); - QVERIFY(compSourceFile.contains(m_dirURL.toString() + "source.cpp")); + QVERIFY(compSourceFile.contains(m_dirURL.toString() + QStringLiteral("source.cpp"))); // But it should also be able to find folders - m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + "file_subdir"); + m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + QStringLiteral("file_subdir")); waitForCompletion(m_completionWithMimeFilter); const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); QCOMPARE(compMimeFolder.count(), 1); - QVERIFY(compMimeFolder.contains(m_dirURL.toString() + "file_subdir/")); + QVERIFY(compMimeFolder.contains(m_dirURL.toString() + QStringLiteral("file_subdir/"))); } void KUrlCompletionTest::testEmptyCwd() { // Completion with empty string (with a KUrlCompletion whose cwd is "") qDebug() << endl << "now completing on '' with empty cwd"; m_completionEmptyCwd->makeCompletion(QLatin1String("")); waitForCompletion(m_completionEmptyCwd); QStringList compEmpty = m_completionEmptyCwd->allMatches(); QCOMPARE(compEmpty.count(), 0); } void KUrlCompletionTest::testBug346920() { m_completionEmptyCwd->makeCompletion(QStringLiteral("~/.")); waitForCompletion(m_completionEmptyCwd); m_completionEmptyCwd->allMatches(); // just don't crash } void KUrlCompletionTest::testInvalidProtocol() { m_completion->makeCompletion(QStringLiteral(":/")); waitForCompletion(m_completion); m_completion->allMatches(); // just don't crash } void KUrlCompletionTest::testUser() { m_completionEmptyCwd->makeCompletion(QStringLiteral("~")); waitForCompletion(m_completionEmptyCwd); const auto matches = m_completionEmptyCwd->allMatches(); if (!KUser::allUserNames().isEmpty()) { Q_ASSERT(!matches.isEmpty()); } foreach (const auto &user, KUser::allUserNames()) { - QVERIFY2(matches.contains(QLatin1Char('~') + user), qPrintable(matches.join(' '))); + QVERIFY2(matches.contains(QLatin1Char('~') + user), qPrintable(matches.join(QLatin1Char(' ')))); } // Check that the same query doesn't re-list m_completionEmptyCwd->makeCompletion(QStringLiteral("~")); QVERIFY(!m_completionEmptyCwd->isRunning()); QCOMPARE(m_completionEmptyCwd->allMatches(), matches); } // Test cancelling a running thread // In a normal run (./kurlcompletiontest) and a reasonable amount of files, we have few chances of making this happen // But in a "nowait" run (./kurlcompletiontest-nowait), this will cancel the thread before it even starts listing the dir. void KUrlCompletionTest::testCancel() { KUrlCompletion comp; comp.setDir(QUrl::fromLocalFile("/usr/bin")); comp.makeCompletion(QStringLiteral("g")); const QStringList matchesG = comp.allMatches(); // We get many matches in a normal run, and usually 0 matches when testing "no wait" (thread is sleeping) -> this is where this method can test cancelling //qDebug() << "got" << matchesG.count() << "matches"; bool done = !comp.isRunning(); // Doing the same search again, should hopefully not restart everything from scratch comp.makeCompletion(QStringLiteral("g")); const QStringList matchesG2 = comp.allMatches(); QVERIFY(matchesG2.count() >= matchesG.count()); if (done) { QVERIFY(!comp.isRunning()); // it had no reason to restart } done = !comp.isRunning(); // Search for something else, should reuse dir listing but not mix up results comp.makeCompletion(QStringLiteral("a")); if (done) { QVERIFY(!comp.isRunning()); // it had no reason to restart } const QStringList matchesA = comp.allMatches(); //qDebug() << "got" << matchesA.count() << "matches"; foreach (const QString &match, matchesA) { - QVERIFY2(!match.startsWith('g'), qPrintable(match)); + QVERIFY2(!match.startsWith(QLatin1Char('g')), qPrintable(match)); } waitForCompletion(&comp); foreach (const QString &match, comp.allMatches()) { - QVERIFY2(!match.startsWith('g'), qPrintable(match)); + QVERIFY2(!match.startsWith(QLatin1Char('g')), qPrintable(match)); } } void KUrlCompletionTest::test() { runAllTests(); // Try again, with another QTemporaryDir (to check that the caching doesn't give us wrong results) runAllTests(); } void KUrlCompletionTest::runAllTests() { setup(); testLocalRelativePath(); testLocalAbsolutePath(); testLocalURL(); testEmptyCwd(); testBug346920(); testInvalidProtocol(); testUser(); testCancel(); teardown(); } QTEST_MAIN(KUrlCompletionTest) #include "kurlcompletiontest.moc" diff --git a/src/ioslaves/trash/tests/CMakeLists.txt b/src/ioslaves/trash/tests/CMakeLists.txt index 66d74c86..d9914d30 100644 --- a/src/ioslaves/trash/tests/CMakeLists.txt +++ b/src/ioslaves/trash/tests/CMakeLists.txt @@ -1,38 +1,35 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ) -remove_definitions(-DQT_NO_CAST_FROM_ASCII) -remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) - ########### next target ############### ecm_qt_declare_logging_category(kio_trash_PART_test_DEBUG_SRCS HEADER kiotrashdebug.h IDENTIFIER KIO_TRASH CATEGORY_NAME kf5.kio.trash) set(testtrash_SRCS testtrash.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../trashimpl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../trashsizecache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../discspaceutil.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../kinterprocesslock.cpp ${kio_trash_PART_test_DEBUG_SRCS} ) include(ECMAddTests) ecm_add_test(${testtrash_SRCS} TEST_NAME testtrash LINK_LIBRARIES KF5::I18n Qt5::DBus KF5::KIOCore KF5::Solid Qt5::Test Qt5::Network ) if(APPLE) target_link_libraries(testtrash "-framework DiskArbitration -framework CoreFoundation") endif(APPLE) # other tests like dropjob and fileundomanager use the trash in ~/.qttest, which this test cleans up set_tests_properties(testtrash PROPERTIES RUN_SERIAL TRUE) ### next target ### add_executable(lockingtest lockingtest.cpp ../kinterprocesslock.cpp) ecm_mark_nongui_executable(lockingtest) target_link_libraries(lockingtest Qt5::Core Qt5::DBus) diff --git a/src/ioslaves/trash/tests/testtrash.cpp b/src/ioslaves/trash/tests/testtrash.cpp index e1c0e5f1..77eb5bc5 100644 --- a/src/ioslaves/trash/tests/testtrash.cpp +++ b/src/ioslaves/trash/tests/testtrash.cpp @@ -1,1337 +1,1337 @@ /* This file is part of the KDE project Copyright (C) 2004 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "testtrash.h" #include #include "kio_trash.h" #include "../../../pathhelpers_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // There are two ways to test encoding things: // * with utf8 filenames // * with latin1 filenames -- not sure this still works. // #define UTF8TEST 1 int initLocale() { #ifdef UTF8TEST // Assume utf8 system setenv("LC_ALL", "C.utf-8", 1); setenv("KDE_UTF8_FILENAMES", "true", 1); #else // Ensure a known QFile::encodeName behavior for trashUtf8FileFromHome // However this assume your $HOME doesn't use characters from other locales... setenv("LC_ALL", "en_US.ISO-8859-1", 1); unsetenv("KDE_UTF8_FILENAMES"); #endif setenv("KIOSLAVE_ENABLE_TESTMODE", "1", 1); // ensure the ioslaves call QStandardPaths::setTestModeEnabled(true) too setenv("KDE_SKIP_KDERC", "1", 1); unsetenv("KDE_COLOR_DEBUG"); return 0; } Q_CONSTRUCTOR_FUNCTION(initLocale) QString TestTrash::homeTmpDir() const { return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/testtrash/"); } QString TestTrash::readOnlyDirPath() const { return homeTmpDir() + QLatin1String("readonly"); } QString TestTrash::otherTmpDir() const { // This one needs to be on another partition for the test to be meaningful QString tempDir = m_tempDir.path(); - if (!tempDir.endsWith('/')) { - tempDir.append('/'); + if (!tempDir.endsWith(QLatin1Char('/'))) { + tempDir.append(QLatin1Char('/')); } return tempDir; } QString TestTrash::utf8FileName() const { return QLatin1String("test") + QChar(0x2153); // "1/3" character, not part of latin1 } QString TestTrash::umlautFileName() const { return QLatin1String("umlaut") + QChar(0xEB); } static void removeFile(const QString &trashDir, const QString &fileName) { QDir dir; dir.remove(trashDir + fileName); QVERIFY(!QDir(trashDir + fileName).exists()); } static void removeDir(const QString &trashDir, const QString &dirName) { QDir dir; dir.rmdir(trashDir + dirName); QVERIFY(!QDir(trashDir + dirName).exists()); } static void removeDirRecursive(const QString &dir) { if (QFile::exists(dir)) { // Make it work even with readonly dirs, like trashReadOnlyDirFromHome() creates QUrl u = QUrl::fromLocalFile(dir); //qDebug() << "chmod +0200 on" << u; KFileItem fileItem(u, QStringLiteral("inode/directory"), KFileItem::Unknown); KFileItemList fileItemList; fileItemList.append(fileItem); KIO::ChmodJob *chmodJob = KIO::chmod(fileItemList, 0200, 0200, QString(), QString(), true /*recursive*/, KIO::HideProgressInfo); chmodJob->exec(); KIO::Job *delJob = KIO::del(u, KIO::HideProgressInfo); if (!delJob->exec()) { qFatal("Couldn't delete %s", qPrintable(dir)); } } } void TestTrash::initTestCase() { // To avoid a runtime dependency on klauncher qputenv("KDE_FORK_SLAVES", "yes"); QStandardPaths::setTestModeEnabled(true); QVERIFY(m_tempDir.isValid()); #ifndef Q_OS_OSX m_trashDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/Trash"); qDebug() << "setup: using trash directory " << m_trashDir; #endif // Look for another writable partition than $HOME (not mandatory) TrashImpl impl; impl.init(); TrashImpl::TrashDirMap trashDirs = impl.trashDirectories(); #ifdef Q_OS_OSX QVERIFY(trashDirs.contains(0)); m_trashDir = trashDirs.value(0); qDebug() << "setup: using trash directory " << m_trashDir; #endif TrashImpl::TrashDirMap topDirs = impl.topDirectories(); bool foundTrashDir = false; m_otherPartitionId = 0; m_tmpIsWritablePartition = false; m_tmpTrashId = -1; QVector writableTopDirs; for (TrashImpl::TrashDirMap::ConstIterator it = trashDirs.constBegin(); it != trashDirs.constEnd(); ++it) { if (it.key() == 0) { QVERIFY(it.value() == m_trashDir); QVERIFY(topDirs.find(0) == topDirs.end()); foundTrashDir = true; } else { QVERIFY(topDirs.find(it.key()) != topDirs.end()); const QString topdir = topDirs[it.key()]; if (QFileInfo(topdir).isWritable()) { writableTopDirs.append(it.key()); if (topdir == QLatin1String("/tmp/")) { m_tmpIsWritablePartition = true; m_tmpTrashId = it.key(); qDebug() << "/tmp is on its own partition (trashid=" << m_tmpTrashId << "), some tests will be skipped"; removeFile(it.value(), QStringLiteral("/info/fileFromOther.trashinfo")); removeFile(it.value(), QStringLiteral("/files/fileFromOther")); removeFile(it.value(), QStringLiteral("/info/symlinkFromOther.trashinfo")); removeFile(it.value(), QStringLiteral("/files/symlinkFromOther")); removeFile(it.value(), QStringLiteral("/info/trashDirFromOther.trashinfo")); removeFile(it.value(), QStringLiteral("/files/trashDirFromOther/testfile")); removeDir(it.value(), QStringLiteral("/files/trashDirFromOther")); } } } } for (QVector::const_iterator it = writableTopDirs.constBegin(); it != writableTopDirs.constEnd(); ++it) { const QString topdir = topDirs[ *it ]; const QString trashdir = trashDirs[ *it ]; QVERIFY(!topdir.isEmpty()); QVERIFY(!trashDirs.isEmpty()); if (topdir != QLatin1String("/tmp/") || // we'd prefer not to use /tmp here, to separate the tests (writableTopDirs.count() > 1)) { // but well, if we have no choice, take it m_otherPartitionTopDir = topdir; m_otherPartitionTrashDir = trashdir; m_otherPartitionId = *it; qDebug() << "OK, found another writable partition: topDir=" << m_otherPartitionTopDir << " trashDir=" << m_otherPartitionTrashDir << " id=" << m_otherPartitionId << endl; break; } } // Check that m_trashDir got listed QVERIFY(foundTrashDir); if (m_otherPartitionTrashDir.isEmpty()) { qWarning() << "No writable partition other than $HOME found, some tests will be skipped"; } // Start with a clean base dir qDebug() << "initial cleanup"; removeDirRecursive(homeTmpDir()); QDir dir; // TT: why not a static method? bool ok = dir.mkdir(homeTmpDir()); if (!ok) { qFatal("Couldn't create directory: %s", qPrintable(homeTmpDir())); } QVERIFY(QFileInfo(otherTmpDir()).isDir()); // Start with a clean trash too qDebug() << "removing trash dir"; removeDirRecursive(m_trashDir); } void TestTrash::cleanupTestCase() { // Clean up removeDirRecursive(homeTmpDir()); removeDirRecursive(otherTmpDir()); removeDirRecursive(m_trashDir); } void TestTrash::urlTestFile() { const QUrl url = TrashImpl::makeURL(1, QStringLiteral("fileId"), QString()); QCOMPARE(url.url(), QStringLiteral("trash:/1-fileId")); int trashId; QString fileId; QString relativePath; bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); QVERIFY(ok); QCOMPARE(QString::number(trashId), QStringLiteral("1")); QCOMPARE(fileId, QStringLiteral("fileId")); QCOMPARE(relativePath, QString()); } void TestTrash::urlTestDirectory() { const QUrl url = TrashImpl::makeURL(1, QStringLiteral("fileId"), QStringLiteral("subfile")); QCOMPARE(url.url(), QStringLiteral("trash:/1-fileId/subfile")); int trashId; QString fileId; QString relativePath; bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); QVERIFY(ok); QCOMPARE(trashId, 1); QCOMPARE(fileId, QStringLiteral("fileId")); QCOMPARE(relativePath, QStringLiteral("subfile")); } void TestTrash::urlTestSubDirectory() { const QUrl url = TrashImpl::makeURL(1, QStringLiteral("fileId"), QStringLiteral("subfile/foobar")); QCOMPARE(url.url(), QStringLiteral("trash:/1-fileId/subfile/foobar")); int trashId; QString fileId; QString relativePath; bool ok = TrashImpl::parseURL(url, trashId, fileId, relativePath); QVERIFY(ok); QCOMPARE(trashId, 1); QCOMPARE(fileId, QStringLiteral("fileId")); QCOMPARE(relativePath, QStringLiteral("subfile/foobar")); } static void checkInfoFile(const QString &infoPath, const QString &origFilePath) { qDebug() << infoPath; QFileInfo info(infoPath); QVERIFY2(info.exists(), qPrintable(infoPath)); QVERIFY(info.isFile()); KConfig infoFile(info.absoluteFilePath()); KConfigGroup group = infoFile.group("Trash Info"); if (!group.exists()) { qFatal("no Trash Info group in %s", qPrintable(info.absoluteFilePath())); } const QString origPath = group.readEntry("Path"); QVERIFY(!origPath.isEmpty()); QCOMPARE(origPath.toUtf8(), QUrl::toPercentEncoding(origFilePath, "/")); if (origFilePath.contains(QChar(0x2153)) || origFilePath.contains(QLatin1Char('%')) || origFilePath.contains(QStringLiteral("umlaut"))) { QVERIFY(origPath.contains(QLatin1Char('%'))); } else { QVERIFY(!origPath.contains(QLatin1Char('%'))); } const QString date = group.readEntry("DeletionDate"); QVERIFY(!date.isEmpty()); QVERIFY(date.contains(QStringLiteral("T"))); } static void createTestFile(const QString &path) { QFile f(path); if (!f.open(QIODevice::WriteOnly)) { qFatal("Can't create %s", qPrintable(path)); } f.write("Hello world\n", 12); f.close(); QVERIFY(QFile::exists(path)); } void TestTrash::trashFile(const QString &origFilePath, const QString &fileId) { // setup if (!QFile::exists(origFilePath)) { createTestFile(origFilePath); } QUrl u = QUrl::fromLocalFile(origFilePath); // test KIO::Job *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); bool ok = job->exec(); if (!ok) { qCritical() << "moving " << u << " to trash failed with error " << job->error() << " " << job->errorString() << endl; } QVERIFY(ok); if (origFilePath.startsWith(QLatin1String("/tmp")) && m_tmpIsWritablePartition) { qDebug() << " TESTS SKIPPED"; } else { checkInfoFile(m_trashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo"), origFilePath); QFileInfo files(m_trashDir + QLatin1String("/files/") + fileId); QVERIFY(files.isFile()); QVERIFY(files.size() == 12); } // coolo suggests testing that the original file is actually gone, too :) QVERIFY(!QFile::exists(origFilePath)); QMap metaData = job->metaData(); QVERIFY(!metaData.isEmpty()); bool found = false; QMap::ConstIterator it = metaData.constBegin(); for (; it != metaData.constEnd(); ++it) { if (it.key().startsWith(QLatin1String("trashURL"))) { QUrl trashURL(it.value()); qDebug() << trashURL; QVERIFY(!trashURL.isEmpty()); QVERIFY(trashURL.scheme() == QLatin1String("trash")); int trashId = 0; if (origFilePath.startsWith(QLatin1String("/tmp")) && m_tmpIsWritablePartition) { trashId = m_tmpTrashId; } QCOMPARE(trashURL.path(), QString(QStringLiteral("/") + QString::number(trashId) + QLatin1Char('-') + fileId)); found = true; } } QVERIFY(found); } void TestTrash::trashFileFromHome() { const QString fileName = QStringLiteral("fileFromHome"); trashFile(homeTmpDir() + fileName, fileName); // Do it again, check that we got a different id trashFile(homeTmpDir() + fileName, fileName + QLatin1String(" (1)")); } void TestTrash::trashPercentFileFromHome() { const QString fileName = QStringLiteral("file%2f"); trashFile(homeTmpDir() + fileName, fileName); } void TestTrash::trashUtf8FileFromHome() { #ifdef UTF8TEST const QString fileName = utf8FileName(); trashFile(homeTmpDir() + fileName, fileName); #endif } void TestTrash::trashUmlautFileFromHome() { const QString fileName = umlautFileName(); trashFile(homeTmpDir() + fileName, fileName); } void TestTrash::testTrashNotEmpty() { KConfig cfg(QStringLiteral("trashrc"), KConfig::SimpleConfig); const KConfigGroup group = cfg.group("Status"); QVERIFY(group.exists()); QVERIFY(group.readEntry("Empty", true) == false); } void TestTrash::trashFileFromOther() { const QString fileName = QStringLiteral("fileFromOther"); trashFile(otherTmpDir() + fileName, fileName); } void TestTrash::trashFileIntoOtherPartition() { if (m_otherPartitionTrashDir.isEmpty()) { qDebug() << " - SKIPPED"; return; } const QString fileName = QStringLiteral("testtrash-file"); const QString origFilePath = m_otherPartitionTopDir + fileName; const QString fileId = fileName; // cleanup QFile::remove(m_otherPartitionTrashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo")); QFile::remove(m_otherPartitionTrashDir + QLatin1String("/files/") + fileId); // setup if (!QFile::exists(origFilePath)) { createTestFile(origFilePath); } QUrl u = QUrl::fromLocalFile(origFilePath); // test KIO::Job *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(ok); QMap metaData = job->metaData(); // Note that the Path stored in the info file is relative, on other partitions (#95652) checkInfoFile(m_otherPartitionTrashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo"), fileName); QFileInfo files(m_otherPartitionTrashDir + QLatin1String("/files/") + fileId); QVERIFY(files.isFile()); QVERIFY(files.size() == 12); // coolo suggests testing that the original file is actually gone, too :) QVERIFY(!QFile::exists(origFilePath)); QVERIFY(!metaData.isEmpty()); bool found = false; QMap::ConstIterator it = metaData.constBegin(); for (; it != metaData.constEnd(); ++it) { if (it.key().startsWith(QLatin1String("trashURL"))) { QUrl trashURL(it.value()); qDebug() << trashURL; QVERIFY(!trashURL.isEmpty()); QVERIFY(trashURL.scheme() == QLatin1String("trash")); QVERIFY(trashURL.path() == QStringLiteral("/%1-%2").arg(m_otherPartitionId).arg(fileId)); found = true; } } QVERIFY(found); } void TestTrash::trashFileOwnedByRoot() { QUrl u(QStringLiteral("file:///etc/passwd")); const QString fileId = QStringLiteral("passwd"); if (geteuid() == 0 || QFileInfo(u.toLocalFile()).isWritable()) { QSKIP("Test must not be run by root."); } KIO::CopyJob *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); job->setUiDelegate(nullptr); // no skip dialog, thanks bool ok = job->exec(); QVERIFY(!ok); QVERIFY(job->error() == KIO::ERR_ACCESS_DENIED); const QString infoPath(m_trashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo")); QVERIFY(!QFile::exists(infoPath)); QFileInfo files(m_trashDir + QLatin1String("/files/") + fileId); QVERIFY(!files.exists()); QVERIFY(QFile::exists(u.path())); } void TestTrash::trashSymlink(const QString &origFilePath, const QString &fileId, bool broken) { // setup const char *target = broken ? "/nonexistent" : "/tmp"; - bool ok = ::symlink(target, QFile::encodeName(origFilePath)) == 0; + bool ok = ::symlink(target, QFile::encodeName(origFilePath).constData()) == 0; QVERIFY(ok); QUrl u = QUrl::fromLocalFile(origFilePath); // test KIO::Job *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); ok = job->exec(); QVERIFY(ok); if (origFilePath.startsWith(QLatin1String("/tmp")) && m_tmpIsWritablePartition) { qDebug() << " TESTS SKIPPED"; return; } checkInfoFile(m_trashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo"), origFilePath); QFileInfo files(m_trashDir + QLatin1String("/files/") + fileId); QVERIFY(files.isSymLink()); QVERIFY(files.readLink() == QFile::decodeName(target)); QVERIFY(!QFile::exists(origFilePath)); } void TestTrash::trashSymlinkFromHome() { const QString fileName = QStringLiteral("symlinkFromHome"); trashSymlink(homeTmpDir() + fileName, fileName, false); } void TestTrash::trashSymlinkFromOther() { const QString fileName = QStringLiteral("symlinkFromOther"); trashSymlink(otherTmpDir() + fileName, fileName, false); } void TestTrash::trashBrokenSymlinkFromHome() { const QString fileName = QStringLiteral("brokenSymlinkFromHome"); trashSymlink(homeTmpDir() + fileName, fileName, true); } void TestTrash::trashDirectory(const QString &origPath, const QString &fileId) { qDebug() << fileId; // setup if (!QFileInfo::exists(origPath)) { QDir dir; bool ok = dir.mkdir(origPath); QVERIFY(ok); } createTestFile(origPath + QLatin1String("/testfile")); QVERIFY(QDir().mkdir(origPath + QStringLiteral("/subdir"))); createTestFile(origPath + QLatin1String("/subdir/subfile")); QUrl u = QUrl::fromLocalFile(origPath); // test KIO::Job *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); QVERIFY(job->exec()); if (origPath.startsWith(QLatin1String("/tmp")) && m_tmpIsWritablePartition) { qDebug() << " TESTS SKIPPED"; return; } checkInfoFile(m_trashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo"), origPath); QFileInfo filesDir(m_trashDir + QLatin1String("/files/") + fileId); QVERIFY(filesDir.isDir()); QFileInfo files(m_trashDir + QLatin1String("/files/") + fileId + QLatin1String("/testfile")); QVERIFY(files.exists()); QVERIFY(files.isFile()); QVERIFY(files.size() == 12); QVERIFY(!QFile::exists(origPath)); QVERIFY(QFile::exists(m_trashDir + QStringLiteral("/files/") + fileId + QStringLiteral("/subdir/subfile"))); QFile dirCache(m_trashDir + QLatin1String("/directorysizes")); QVERIFY2(dirCache.open(QIODevice::ReadOnly), qPrintable(dirCache.fileName())); QByteArray lines; bool found = false; while (!dirCache.atEnd()) { const QByteArray line = dirCache.readLine(); if (line.endsWith(' ' + QFile::encodeName(fileId).toPercentEncoding() + '\n')) { QVERIFY(!found); // should be there only once! found = true; } lines += line; } QVERIFY2(found, lines.constData()); //qDebug() << lines; checkDirCacheValidity(); } void TestTrash::checkDirCacheValidity() { QFile dirCache(m_trashDir + QLatin1String("/directorysizes")); QVERIFY2(dirCache.open(QIODevice::ReadOnly), qPrintable(dirCache.fileName())); QSet seenDirs; while (!dirCache.atEnd()) { QByteArray line = dirCache.readLine(); QVERIFY(line.endsWith('\n')); line.chop(1); qDebug() << "LINE" << line; const int lastSpace = line.lastIndexOf(' '); const QByteArray dir = QByteArray::fromPercentEncoding(line.mid(lastSpace + 1)); QVERIFY2(!seenDirs.contains(dir), dir.constData()); seenDirs.insert(dir); const QString localDir = m_trashDir + QLatin1String("/files/") + QFile::decodeName(dir); QVERIFY2(QFile::exists(localDir), qPrintable(localDir)); QVERIFY(QFileInfo(localDir).isDir()); } } void TestTrash::trashDirectoryFromHome() { QString dirName = QStringLiteral("trashDirFromHome"); trashDirectory(homeTmpDir() + dirName, dirName); // Do it again, check that we got a different id trashDirectory(homeTmpDir() + dirName, dirName + QLatin1String(" (1)")); } void TestTrash::trashDotDirectory() { QString dirName = QStringLiteral(".dotTrashDirFromHome"); trashDirectory(homeTmpDir() + dirName, dirName); // Do it again, check that we got a different id // TODO trashDirectory(homeTmpDir() + dirName, dirName + QString::fromLatin1(" (1)")); } void TestTrash::trashReadOnlyDirFromHome() { const QString dirName = readOnlyDirPath(); QDir dir; bool ok = dir.mkdir(dirName); QVERIFY(ok); // #130780 const QString subDirPath = dirName + QLatin1String("/readonly_subdir"); ok = dir.mkdir(subDirPath); QVERIFY(ok); createTestFile(subDirPath + QLatin1String("/testfile_in_subdir")); - ::chmod(QFile::encodeName(subDirPath), 0500); + ::chmod(QFile::encodeName(subDirPath).constData(), 0500); trashDirectory(dirName, QStringLiteral("readonly")); } void TestTrash::trashDirectoryFromOther() { QString dirName = QStringLiteral("trashDirFromOther"); trashDirectory(otherTmpDir() + dirName, dirName); } void TestTrash::trashDirectoryWithTrailingSlash() { QString dirName = QStringLiteral("dirwithslash/"); trashDirectory(homeTmpDir() + dirName, QStringLiteral("dirwithslash")); } void TestTrash::trashBrokenSymlinkIntoSubdir() { QString origPath = homeTmpDir() + QStringLiteral("subDirBrokenSymlink"); if (!QFileInfo::exists(origPath)) { QDir dir; bool ok = dir.mkdir(origPath); QVERIFY(ok); } - bool ok = ::symlink("/nonexistent", QFile::encodeName(origPath + "/link")) == 0; + bool ok = ::symlink("/nonexistent", QFile::encodeName(origPath + QStringLiteral("/link")).constData()) == 0; QVERIFY(ok); trashDirectory(origPath, QStringLiteral("subDirBrokenSymlink")); } void TestTrash::delRootFile() { // test deleting a trashed file KIO::Job *delJob = KIO::del(QUrl(QStringLiteral("trash:/0-fileFromHome")), KIO::HideProgressInfo); bool ok = delJob->exec(); QVERIFY(ok); QFileInfo file(m_trashDir + QLatin1String("/files/fileFromHome")); QVERIFY(!file.exists()); QFileInfo info(m_trashDir + QLatin1String("/info/fileFromHome.trashinfo")); QVERIFY(!info.exists()); // trash it again, we might need it later const QString fileName = QStringLiteral("fileFromHome"); trashFile(homeTmpDir() + fileName, fileName); } void TestTrash::delFileInDirectory() { // test deleting a file inside a trashed directory -> not allowed KIO::Job *delJob = KIO::del(QUrl(QStringLiteral("trash:/0-trashDirFromHome/testfile")), KIO::HideProgressInfo); bool ok = delJob->exec(); QVERIFY(!ok); QVERIFY(delJob->error() == KIO::ERR_ACCESS_DENIED); QFileInfo dir(m_trashDir + QLatin1String("/files/trashDirFromHome")); QVERIFY(dir.exists()); QFileInfo file(m_trashDir + QLatin1String("/files/trashDirFromHome/testfile")); QVERIFY(file.exists()); QFileInfo info(m_trashDir + QLatin1String("/info/trashDirFromHome.trashinfo")); QVERIFY(info.exists()); } void TestTrash::delDirectory() { // test deleting a trashed directory KIO::Job *delJob = KIO::del(QUrl(QStringLiteral("trash:/0-trashDirFromHome")), KIO::HideProgressInfo); bool ok = delJob->exec(); QVERIFY(ok); QFileInfo dir(m_trashDir + QLatin1String("/files/trashDirFromHome")); QVERIFY(!dir.exists()); QFileInfo file(m_trashDir + QLatin1String("/files/trashDirFromHome/testfile")); QVERIFY(!file.exists()); QFileInfo info(m_trashDir + QLatin1String("/info/trashDirFromHome.trashinfo")); QVERIFY(!info.exists()); // trash it again, we'll need it later QString dirName = QStringLiteral("trashDirFromHome"); trashDirectory(homeTmpDir() + dirName, dirName); } static bool MyNetAccess_stat(const QUrl &url, KIO::UDSEntry &entry) { KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); bool ok = statJob->exec(); if (ok) { entry = statJob->statResult(); } return ok; } static bool MyNetAccess_exists(const QUrl &url) { KIO::UDSEntry dummy; return MyNetAccess_stat(url, dummy); } void TestTrash::statRoot() { QUrl url(QStringLiteral("trash:/")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isDir()); QVERIFY(!item.isLink()); QVERIFY(item.isReadable()); QVERIFY(item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.name(), QStringLiteral(".")); } void TestTrash::statFileInRoot() { QUrl url(QStringLiteral("trash:/0-fileFromHome")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isFile()); QVERIFY(!item.isDir()); QVERIFY(!item.isLink()); QVERIFY(item.isReadable()); QVERIFY(!item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.text(), QStringLiteral("fileFromHome")); } void TestTrash::statDirectoryInRoot() { QUrl url(QStringLiteral("trash:/0-trashDirFromHome")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isDir()); QVERIFY(!item.isLink()); QVERIFY(item.isReadable()); QVERIFY(!item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.text(), QStringLiteral("trashDirFromHome")); } void TestTrash::statSymlinkInRoot() { QUrl url(QStringLiteral("trash:/0-symlinkFromHome")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isLink()); QCOMPARE(item.linkDest(), QStringLiteral("/tmp")); QVERIFY(item.isReadable()); QVERIFY(!item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.text(), QStringLiteral("symlinkFromHome")); } void TestTrash::statFileInDirectory() { QUrl url(QStringLiteral("trash:/0-trashDirFromHome/testfile")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isFile()); QVERIFY(!item.isLink()); QVERIFY(item.isReadable()); QVERIFY(!item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.text(), QStringLiteral("testfile")); } void TestTrash::statBrokenSymlinkInSubdir() { QUrl url(QStringLiteral("trash:/0-subDirBrokenSymlink/link")); KIO::UDSEntry entry; bool ok = MyNetAccess_stat(url, entry); QVERIFY(ok); KFileItem item(entry, url); QVERIFY(item.isLink()); QVERIFY(item.isReadable()); QVERIFY(!item.isWritable()); QVERIFY(!item.isHidden()); QCOMPARE(item.linkDest(), QLatin1String("/nonexistent")); } void TestTrash::copyFromTrash(const QString &fileId, const QString &destPath, const QString &relativePath) { QUrl src(QLatin1String("trash:/0-") + fileId); if (!relativePath.isEmpty()) { src.setPath(concatPaths(src.path(), relativePath)); } QUrl dest = QUrl::fromLocalFile(destPath); QVERIFY(MyNetAccess_exists(src)); // A dnd would use copy(), but we use copyAs to ensure the final filename //qDebug() << "copyAs:" << src << " -> " << dest; KIO::Job *job = KIO::copyAs(src, dest, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY2(ok, qPrintable(job->errorString())); QString infoFile(m_trashDir + QLatin1String("/info/") + fileId + QLatin1String(".trashinfo")); QVERIFY(QFile::exists(infoFile)); QFileInfo filesItem(m_trashDir + QLatin1String("/files/") + fileId); QVERIFY(filesItem.exists()); QVERIFY(QFile::exists(destPath)); } void TestTrash::copyFileFromTrash() { // To test case of already-existing destination, uncomment this. // This brings up the "rename" dialog though, so it can't be fully automated #if 0 const QString destPath = otherTmpDir() + QString::fromLatin1("fileFromHome_copied"); copyFromTrash("fileFromHome", destPath); QVERIFY(QFileInfo(destPath).isFile()); QVERIFY(QFileInfo(destPath).size() == 12); #endif } void TestTrash::copyFileInDirectoryFromTrash() { const QString destPath = otherTmpDir() + QLatin1String("testfile_copied"); copyFromTrash(QStringLiteral("trashDirFromHome"), destPath, QStringLiteral("testfile")); QVERIFY(QFileInfo(destPath).isFile()); QVERIFY(QFileInfo(destPath).size() == 12); QVERIFY(QFileInfo(destPath).isWritable()); } void TestTrash::copyDirectoryFromTrash() { const QString destPath = otherTmpDir() + QLatin1String("trashDirFromHome_copied"); copyFromTrash(QStringLiteral("trashDirFromHome"), destPath); QVERIFY(QFileInfo(destPath).isDir()); - QVERIFY(QFile::exists(destPath + "/testfile")); - QVERIFY(QFile::exists(destPath + "/subdir/subfile")); + QVERIFY(QFile::exists(destPath + QStringLiteral("/testfile"))); + QVERIFY(QFile::exists(destPath + QStringLiteral("/subdir/subfile"))); } void TestTrash::copySymlinkFromTrash() // relies on trashSymlinkFromHome() being called first { const QString destPath = otherTmpDir() + QLatin1String("symlinkFromHome_copied"); copyFromTrash(QStringLiteral("symlinkFromHome"), destPath); QVERIFY(QFileInfo(destPath).isSymLink()); } void TestTrash::moveInTrash(const QString &fileId, const QString &destFileId) { const QUrl src(QLatin1String("trash:/0-") + fileId); const QUrl dest(QLatin1String("trash:/") + destFileId); QVERIFY(MyNetAccess_exists(src)); QVERIFY(!MyNetAccess_exists(dest)); KIO::Job *job = KIO::moveAs(src, dest, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY2(ok, qPrintable(job->errorString())); // Check old doesn't exist anymore - QString infoFile(m_trashDir + "/info/" + fileId + ".trashinfo"); + QString infoFile(m_trashDir + QStringLiteral("/info/") + fileId + QStringLiteral(".trashinfo")); QVERIFY(!QFile::exists(infoFile)); - QFileInfo filesItem(m_trashDir + "/files/" + fileId); + QFileInfo filesItem(m_trashDir + QStringLiteral("/files/") + fileId); QVERIFY(!filesItem.exists()); // Check new exists now - QString newInfoFile(m_trashDir + "/info/" + destFileId + ".trashinfo"); + QString newInfoFile(m_trashDir + QStringLiteral("/info/") + destFileId + QStringLiteral(".trashinfo")); QVERIFY(QFile::exists(newInfoFile)); - QFileInfo newFilesItem(m_trashDir + "/files/" + destFileId); + QFileInfo newFilesItem(m_trashDir + QStringLiteral("/files/") + destFileId); QVERIFY(newFilesItem.exists()); } void TestTrash::renameFileInTrash() { const QString fileName = QStringLiteral("renameFileInTrash"); const QString filePath = homeTmpDir() + fileName; createTestFile(filePath); trashFile(filePath, fileName); const QString destFileName = QStringLiteral("fileRenamed"); moveInTrash(fileName, destFileName); // cleanup KIO::Job *delJob = KIO::del(QUrl(QStringLiteral("trash:/0-fileRenamed")), KIO::HideProgressInfo); bool ok = delJob->exec(); QVERIFY2(ok, qPrintable(delJob->errorString())); } void TestTrash::renameDirInTrash() { const QString dirName = QStringLiteral("trashDirFromHome"); const QString destDirName = QStringLiteral("dirRenamed"); moveInTrash(dirName, destDirName); moveInTrash(destDirName, dirName); } void TestTrash::moveFromTrash(const QString &fileId, const QString &destPath, const QString &relativePath) { QUrl src(QLatin1String("trash:/0-") + fileId); if (!relativePath.isEmpty()) { src.setPath(concatPaths(src.path(), relativePath)); } QUrl dest = QUrl::fromLocalFile(destPath); QVERIFY(MyNetAccess_exists(src)); // A dnd would use move(), but we use moveAs to ensure the final filename KIO::Job *job = KIO::moveAs(src, dest, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY2(ok, qPrintable(job->errorString())); - QString infoFile(m_trashDir + "/info/" + fileId + ".trashinfo"); + QString infoFile(m_trashDir + QStringLiteral("/info/") + fileId + QStringLiteral(".trashinfo")); QVERIFY(!QFile::exists(infoFile)); - QFileInfo filesItem(m_trashDir + "/files/" + fileId); + QFileInfo filesItem(m_trashDir + QStringLiteral("/files/") + fileId); QVERIFY(!filesItem.exists()); QVERIFY(QFile::exists(destPath)); QVERIFY(QFileInfo(destPath).isWritable()); } void TestTrash::moveFileFromTrash() { const QString fileName = QStringLiteral("moveFileFromTrash"); const QString filePath = homeTmpDir() + fileName; createTestFile(filePath); const QFile::Permissions origPerms = QFileInfo(filePath).permissions(); trashFile(filePath, fileName); - const QString destPath = otherTmpDir() + "fileFromTrash_restored"; + const QString destPath = otherTmpDir() + QStringLiteral("fileFromTrash_restored"); moveFromTrash(fileName, destPath); const QFileInfo destInfo(destPath); QVERIFY(destInfo.isFile()); QCOMPARE(destInfo.size(), 12); QVERIFY(destInfo.isWritable()); QCOMPARE(int(destInfo.permissions()), int(origPerms)); QVERIFY(QFile::remove(destPath)); } void TestTrash::moveFileFromTrashToDir_data() { QTest::addColumn("destDir"); QTest::newRow("home_partition") << homeTmpDir(); // this will trigger a direct renaming QTest::newRow("other_partition") << otherTmpDir(); // this will require a real move } void TestTrash::moveFileFromTrashToDir() { // Given a file in the trash const QString fileName = QStringLiteral("moveFileFromTrashToDir"); const QString filePath = homeTmpDir() + fileName; createTestFile(filePath); const QFile::Permissions origPerms = QFileInfo(filePath).permissions(); trashFile(filePath, fileName); QVERIFY(!QFile::exists(filePath)); // When moving it out to a dir QFETCH(QString, destDir); - const QString destPath = destDir + "moveFileFromTrashToDir"; + const QString destPath = destDir + QStringLiteral("moveFileFromTrashToDir"); const QUrl src(QLatin1String("trash:/0-") + fileName); const QUrl dest(QUrl::fromLocalFile(destDir)); KIO::Job *job = KIO::move(src, dest, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY2(ok, qPrintable(job->errorString())); // Then it should move ;) const QFileInfo destInfo(destPath); QVERIFY(destInfo.isFile()); QCOMPARE(destInfo.size(), 12); QVERIFY(destInfo.isWritable()); QCOMPARE(int(destInfo.permissions()), int(origPerms)); QVERIFY(QFile::remove(destPath)); } void TestTrash::moveFileInDirectoryFromTrash() { - const QString destPath = otherTmpDir() + "testfile_restored"; + const QString destPath = otherTmpDir() + QStringLiteral("testfile_restored"); copyFromTrash(QStringLiteral("trashDirFromHome"), destPath, QStringLiteral("testfile")); QVERIFY(QFileInfo(destPath).isFile()); QVERIFY(QFileInfo(destPath).size() == 12); } void TestTrash::moveDirectoryFromTrash() { - const QString destPath = otherTmpDir() + "trashDirFromHome_restored"; + const QString destPath = otherTmpDir() + QStringLiteral("trashDirFromHome_restored"); moveFromTrash(QStringLiteral("trashDirFromHome"), destPath); QVERIFY(QFileInfo(destPath).isDir()); checkDirCacheValidity(); // trash it again, we'll need it later QString dirName = QStringLiteral("trashDirFromHome"); trashDirectory(homeTmpDir() + dirName, dirName); } void TestTrash::trashDirectoryOwnedByRoot() { QUrl u(QStringLiteral("file:///"));; if (QFile::exists(QStringLiteral("/etc/cups"))) { u.setPath(QStringLiteral("/etc/cups")); } else if (QFile::exists(QStringLiteral("/boot"))) { u.setPath(QStringLiteral("/boot")); } else { u.setPath(QStringLiteral("/etc")); } const QString fileId = u.path(); qDebug() << "fileId=" << fileId; if (geteuid() == 0 || QFileInfo(u.toLocalFile()).isWritable()) { QSKIP("Test must not be run by root."); } KIO::CopyJob *job = KIO::move(u, QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); job->setUiDelegate(nullptr); // no skip dialog, thanks bool ok = job->exec(); QVERIFY(!ok); const int err = job->error(); QVERIFY(err == KIO::ERR_ACCESS_DENIED || err == KIO::ERR_CANNOT_OPEN_FOR_READING); - const QString infoPath(m_trashDir + "/info/" + fileId + ".trashinfo"); + const QString infoPath(m_trashDir + QStringLiteral("/info/") + fileId + QStringLiteral(".trashinfo")); QVERIFY(!QFile::exists(infoPath)); - QFileInfo files(m_trashDir + "/files/" + fileId); + QFileInfo files(m_trashDir + QStringLiteral("/files/") + fileId); QVERIFY(!files.exists()); QVERIFY(QFile::exists(u.path())); } void TestTrash::moveSymlinkFromTrash() { - const QString destPath = otherTmpDir() + "symlinkFromHome_restored"; + const QString destPath = otherTmpDir() + QStringLiteral("symlinkFromHome_restored"); moveFromTrash(QStringLiteral("symlinkFromHome"), destPath); QVERIFY(QFileInfo(destPath).isSymLink()); } void TestTrash::getFile() { const QString fileId = QStringLiteral("fileFromHome (1)"); const QUrl url = TrashImpl::makeURL(0, fileId, QString()); QTemporaryFile tmpFile; QVERIFY(tmpFile.open()); const QString tmpFilePath = tmpFile.fileName(); KIO::Job *getJob = KIO::file_copy(url, QUrl::fromLocalFile(tmpFilePath), -1, KIO::Overwrite | KIO::HideProgressInfo); bool ok = getJob->exec(); QVERIFY2(ok, qPrintable(getJob->errorString())); // Don't use tmpFile.close()+tmpFile.open() here, the size would still be 0 in the QTemporaryFile object // (due to the use of fstat on the old fd). Arguably a bug (I even have a testcase), but probably // not fixable without breaking the security of QTemporaryFile... QFile reader(tmpFilePath); QVERIFY(reader.open(QIODevice::ReadOnly)); QByteArray str = reader.readAll(); QCOMPARE(str, QByteArray("Hello world\n")); } void TestTrash::restoreFile() { const QString fileId = QStringLiteral("fileFromHome (1)"); const QUrl url = TrashImpl::makeURL(0, fileId, QString()); - const QString infoFile(m_trashDir + "/info/" + fileId + ".trashinfo"); - const QString filesItem(m_trashDir + "/files/" + fileId); + const QString infoFile(m_trashDir + QStringLiteral("/info/") + fileId + QStringLiteral(".trashinfo")); + const QString filesItem(m_trashDir + QStringLiteral("/files/") + fileId); QVERIFY(QFile::exists(infoFile)); QVERIFY(QFile::exists(filesItem)); QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << (int)3 << url; KIO::Job *job = KIO::special(url, packedArgs, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(ok); QVERIFY(!QFile::exists(infoFile)); QVERIFY(!QFile::exists(filesItem)); - const QString destPath = homeTmpDir() + "fileFromHome"; + const QString destPath = homeTmpDir() + QStringLiteral("fileFromHome"); QVERIFY(QFile::exists(destPath)); } void TestTrash::restoreFileFromSubDir() { const QString fileId = QStringLiteral("trashDirFromHome (1)/testfile"); - QVERIFY(!QFile::exists(homeTmpDir() + "trashDirFromHome (1)")); + QVERIFY(!QFile::exists(homeTmpDir() + QStringLiteral("trashDirFromHome (1)"))); const QUrl url = TrashImpl::makeURL(0, fileId, QString()); - const QString infoFile(m_trashDir + "/info/trashDirFromHome (1).trashinfo"); - const QString filesItem(m_trashDir + "/files/trashDirFromHome (1)/testfile"); + const QString infoFile(m_trashDir + QStringLiteral("/info/trashDirFromHome (1).trashinfo")); + const QString filesItem(m_trashDir + QStringLiteral("/files/trashDirFromHome (1)/testfile")); QVERIFY(QFile::exists(infoFile)); QVERIFY(QFile::exists(filesItem)); QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << (int)3 << url; KIO::Job *job = KIO::special(url, packedArgs, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(!ok); // dest dir doesn't exist -> error message QVERIFY(job->error() == KIO::ERR_SLAVE_DEFINED); // check that nothing happened QVERIFY(QFile::exists(infoFile)); QVERIFY(QFile::exists(filesItem)); - QVERIFY(!QFile::exists(homeTmpDir() + "trashDirFromHome (1)")); + QVERIFY(!QFile::exists(homeTmpDir() + QStringLiteral("trashDirFromHome (1)"))); } void TestTrash::restoreFileToDeletedDirectory() { // Ensure we'll get "fileFromHome" as fileId removeFile(m_trashDir, QStringLiteral("/info/fileFromHome.trashinfo")); removeFile(m_trashDir, QStringLiteral("/files/fileFromHome")); trashFileFromHome(); // Delete orig dir KIO::Job *delJob = KIO::del(QUrl::fromLocalFile(homeTmpDir()), KIO::HideProgressInfo); bool delOK = delJob->exec(); QVERIFY(delOK); const QString fileId = QStringLiteral("fileFromHome"); const QUrl url = TrashImpl::makeURL(0, fileId, QString()); - const QString infoFile(m_trashDir + "/info/" + fileId + ".trashinfo"); - const QString filesItem(m_trashDir + "/files/" + fileId); + const QString infoFile(m_trashDir + QStringLiteral("/info/") + fileId + QStringLiteral(".trashinfo")); + const QString filesItem(m_trashDir + QStringLiteral("/files/") + fileId); QVERIFY(QFile::exists(infoFile)); QVERIFY(QFile::exists(filesItem)); QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << (int)3 << url; KIO::Job *job = KIO::special(url, packedArgs, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(!ok); // dest dir doesn't exist -> error message QVERIFY(job->error() == KIO::ERR_SLAVE_DEFINED); // check that nothing happened QVERIFY(QFile::exists(infoFile)); QVERIFY(QFile::exists(filesItem)); - const QString destPath = homeTmpDir() + "fileFromHome"; + const QString destPath = homeTmpDir() + QStringLiteral("fileFromHome"); QVERIFY(!QFile::exists(destPath)); } void TestTrash::listRootDir() { m_entryCount = 0; m_listResult.clear(); m_displayNameListResult.clear(); KIO::ListJob *job = KIO::listDir(QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); connect(job, &KIO::ListJob::entries, this, &TestTrash::slotEntries); bool ok = job->exec(); QVERIFY(ok); qDebug() << "listDir done - m_entryCount=" << m_entryCount; QVERIFY(m_entryCount > 1); //qDebug() << m_listResult; //qDebug() << m_displayNameListResult; QCOMPARE(m_listResult.count(QStringLiteral(".")), 1); // found it, and only once QCOMPARE(m_displayNameListResult.count(QStringLiteral("fileFromHome")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("fileFromHome (1)")), 1); } void TestTrash::listRecursiveRootDir() { m_entryCount = 0; m_listResult.clear(); m_displayNameListResult.clear(); KIO::ListJob *job = KIO::listRecursive(QUrl(QStringLiteral("trash:/")), KIO::HideProgressInfo); connect(job, &KIO::ListJob::entries, this, &TestTrash::slotEntries); bool ok = job->exec(); QVERIFY(ok); qDebug() << "listDir done - m_entryCount=" << m_entryCount; QVERIFY(m_entryCount > 1); qDebug() << m_listResult; qDebug() << m_displayNameListResult; QCOMPARE(m_listResult.count(QStringLiteral(".")), 1); // found it, and only once QCOMPARE(m_listResult.count(QStringLiteral("0-fileFromHome")), 1); QCOMPARE(m_listResult.count(QStringLiteral("0-fileFromHome (1)")), 1); QCOMPARE(m_listResult.count(QStringLiteral("0-trashDirFromHome/testfile")), 1); QCOMPARE(m_listResult.count(QStringLiteral("0-readonly/readonly_subdir/testfile_in_subdir")), 1); QCOMPARE(m_listResult.count(QStringLiteral("0-subDirBrokenSymlink/link")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("fileFromHome")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("fileFromHome (1)")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("trashDirFromHome/testfile")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("readonly/readonly_subdir/testfile_in_subdir")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("subDirBrokenSymlink/link")), 1); } void TestTrash::listSubDir() { m_entryCount = 0; m_listResult.clear(); m_displayNameListResult.clear(); KIO::ListJob *job = KIO::listDir(QUrl(QStringLiteral("trash:/0-trashDirFromHome")), KIO::HideProgressInfo); connect(job, &KIO::ListJob::entries, this, &TestTrash::slotEntries); bool ok = job->exec(); QVERIFY(ok); qDebug() << "listDir done - m_entryCount=" << m_entryCount; QCOMPARE(m_entryCount, 3); //qDebug() << m_listResult; //qDebug() << m_displayNameListResult; QCOMPARE(m_listResult.count(QStringLiteral(".")), 1); // found it, and only once QCOMPARE(m_listResult.count(QStringLiteral("testfile")), 1); // found it, and only once QCOMPARE(m_listResult.count(QStringLiteral("subdir")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("testfile")), 1); QCOMPARE(m_displayNameListResult.count(QStringLiteral("subdir")), 1); } void TestTrash::slotEntries(KIO::Job *, const KIO::UDSEntryList &lst) { for (KIO::UDSEntryList::ConstIterator it = lst.begin(); it != lst.end(); ++it) { const KIO::UDSEntry &entry(*it); QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); QUrl url(entry.stringValue(KIO::UDSEntry::UDS_URL)); qDebug() << "name" << name << "displayName" << displayName << " UDS_URL=" << url; if (!url.isEmpty()) { QVERIFY(url.scheme() == QStringLiteral("trash")); } m_listResult << name; m_displayNameListResult << displayName; } m_entryCount += lst.count(); } void TestTrash::emptyTrash() { // ## Even though we use a custom XDG_DATA_HOME value, emptying the // trash would still empty the other trash directories in other partitions. // So we can't activate this test by default. #if 0 // To make this test standalone trashFileFromHome(); // #167051: orphaned files createTestFile(m_trashDir + "/files/testfile_nometadata"); QByteArray packedArgs; QDataStream stream(&packedArgs, QIODevice::WriteOnly); stream << (int)1; KIO::Job *job = KIO::special(QUrl("trash:/"), packedArgs, KIO::HideProgressInfo); bool ok = job->exec(); QVERIFY(ok); KConfig cfg("trashrc", KConfig::SimpleConfig); QVERIFY(cfg.hasGroup("Status")); QVERIFY(cfg.group("Status").readEntry("Empty", false) == true); QVERIFY(!QFile::exists(m_trashDir + "/files/fileFromHome")); QVERIFY(!QFile::exists(m_trashDir + "/files/readonly")); QVERIFY(!QFile::exists(m_trashDir + "/info/readonly.trashinfo")); QVERIFY(QDir(m_trashDir + "/info").entryList(QDir::NoDotAndDotDot | QDir::AllEntries).isEmpty()); QVERIFY(QDir(m_trashDir + "/files").entryList(QDir::NoDotAndDotDot | QDir::AllEntries).isEmpty()); #else qDebug() << " : SKIPPED"; #endif } static bool isTrashEmpty() { KConfig cfg(QStringLiteral("trashrc"), KConfig::SimpleConfig); const KConfigGroup group = cfg.group("Status"); return group.readEntry("Empty", true); } void TestTrash::testEmptyTrashSize() { KIO::DirectorySizeJob *job = KIO::directorySize(QUrl(QStringLiteral("trash:/"))); QVERIFY(job->exec()); if (isTrashEmpty()) { QCOMPARE(job->totalSize(), 0ULL); } else { QVERIFY(job->totalSize() < 1000000000 /*1GB*/); // #157023 } } static void checkIcon(const QUrl &url, const QString &expectedIcon) { QString icon = KIO::iconNameForUrl(url); // #100321 QCOMPARE(icon, expectedIcon); } void TestTrash::testIcons() { // The JSON file says "user-trash-full" in all cases, whether the trash is full or not QCOMPARE(KProtocolInfo::icon(QStringLiteral("trash")), QStringLiteral("user-trash-full")); // #100321 if (isTrashEmpty()) { checkIcon(QUrl(QStringLiteral("trash:/")), QStringLiteral("user-trash")); } else { checkIcon(QUrl(QStringLiteral("trash:/")), QStringLiteral("user-trash-full")); } checkIcon(QUrl(QStringLiteral("trash:/foo/")), QStringLiteral("inode-directory")); } QTEST_GUILESS_MAIN(TestTrash)