File indexing completed on 2024-12-22 05:16:05

0001 /*
0002     SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <chrono>
0008 
0009 #include <KDirNotify>
0010 #include <KIO/CopyJob>
0011 #include <KIO/FileCopyJob>
0012 #include <KIO/FileUndoManager>
0013 #include <KIO/StatJob>
0014 #include <QDesktopServices>
0015 #include <QProcess>
0016 #include <QSignalSpy>
0017 #include <QStandardPaths>
0018 #include <QTemporaryFile>
0019 #include <QTest>
0020 #include <kdirlister.h>
0021 #include <kio/copyjob.h>
0022 #include <kio/job.h>
0023 
0024 using namespace Qt::StringLiterals;
0025 using namespace std::chrono_literals;
0026 
0027 static void doUndo() // see FileUndoManagerTest::doUndo()
0028 {
0029     QEventLoop eventLoop;
0030     QObject::connect(KIO::FileUndoManager::self(), &KIO::FileUndoManager::undoJobFinished, &eventLoop, &QEventLoop::quit);
0031     KIO::FileUndoManager::self()->undo();
0032     eventLoop.exec(QEventLoop::ExcludeUserInputEvents); // wait for undo job to finish
0033 }
0034 
0035 class TestDesktop : public QObject
0036 {
0037     Q_OBJECT
0038 
0039 public:
0040     TestDesktop()
0041     {
0042     }
0043 
0044 private Q_SLOTS:
0045     void initTestCase()
0046     {
0047         // make KIOs use test mode too
0048         setenv("KIOSLAVE_ENABLE_TESTMODE", "1", 1);
0049         QStandardPaths::setTestModeEnabled(true);
0050 
0051         // Warning: even with test mode enabled, this is the real user's Desktop directory
0052         m_desktopPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
0053         m_testFileName = QLatin1String("kio_desktop_test_file");
0054         qWarning() << "We will write in the desktop folder" << m_desktopPath;
0055         // NOTE: we must shut down kded if we started it in the CI! Do not detach!
0056         m_kded.setProgram("kded6"_L1);
0057         m_kded.start();
0058         const auto started = m_kded.waitForStarted((1000ms).count());
0059         qWarning() << "Starting kded" << started;
0060         cleanupTestCase();
0061     }
0062     void cleanupTestCase()
0063     {
0064         QFile::remove(m_desktopPath + '/' + m_testFileName);
0065         QFile::remove(m_desktopPath + '/' + m_testFileName + ".part");
0066         QFile::remove(m_desktopPath + '/' + m_testFileName + "_link");
0067         m_kded.kill();
0068     }
0069 
0070     void testCopyToDesktop()
0071     {
0072         QTemporaryFile tempFile;
0073         QVERIFY(tempFile.open());
0074         tempFile.write("Hello world\n", 12);
0075         QString fileName = tempFile.fileName();
0076         tempFile.close();
0077         KIO::Job *job = KIO::file_copy(QUrl::fromLocalFile(fileName), QUrl("desktop:/" + m_testFileName), -1, KIO::HideProgressInfo);
0078         job->setUiDelegate(nullptr);
0079         QVERIFY(job->exec());
0080         QVERIFY(QFile::exists(m_desktopPath + '/' + m_testFileName));
0081     }
0082 
0083     void testMostLocalUrl() // relies on testCopyToDesktop being run before
0084     {
0085         const QUrl desktopUrl("desktop:/" + m_testFileName);
0086         const QString filePath(m_desktopPath + '/' + m_testFileName);
0087         KIO::StatJob *job = KIO::mostLocalUrl(desktopUrl, KIO::HideProgressInfo);
0088         QVERIFY(job);
0089         bool ok = job->exec();
0090         QVERIFY(ok);
0091         QCOMPARE(job->mostLocalUrl().toLocalFile(), filePath);
0092     }
0093 
0094     void testCreateSymlink()
0095     {
0096         const QUrl desktopUrl("desktop:/" + m_testFileName);
0097         const QUrl desktopLink("desktop:/" + m_testFileName + "_link");
0098         const QString source = m_desktopPath + '/' + m_testFileName;
0099         const QString localLink = source + "_link";
0100 
0101         // Create a symlink using kio_desktop
0102         KIO::Job *linkJob = KIO::symlink(m_testFileName, desktopLink, KIO::HideProgressInfo);
0103         QVERIFY(linkJob->exec());
0104         QVERIFY(QFileInfo(localLink).isSymLink());
0105         QCOMPARE(QFileInfo(localLink).symLinkTarget(), source);
0106 
0107         // Now try changing the link target, without Overwrite -> error
0108         linkJob = KIO::symlink(m_testFileName + QLatin1Char('2'), desktopLink, KIO::HideProgressInfo);
0109         QVERIFY(!linkJob->exec());
0110         QCOMPARE(linkJob->error(), (int)KIO::ERR_FILE_ALREADY_EXIST);
0111 
0112         // Now try changing the link target, with Overwrite (bug 360487)
0113         linkJob = KIO::symlink(m_testFileName + QLatin1Char('3'), desktopLink, KIO::Overwrite | KIO::HideProgressInfo);
0114         QVERIFY(linkJob->exec());
0115         QVERIFY(QFileInfo(localLink).isSymLink());
0116         QCOMPARE(QFileInfo(localLink).symLinkTarget(), source + QLatin1Char('3'));
0117     }
0118 
0119     void testRename_data()
0120     {
0121         QTest::addColumn<bool>("withDirListerCache");
0122         QTest::addColumn<QUrl>("srcUrl");
0123         QTest::addColumn<QUrl>("destUrl");
0124 
0125         const QString str = "desktop:/" + m_testFileName;
0126         const QUrl orig(str);
0127         const QUrl part(str + ".part");
0128         QTest::newRow("orig_to_part") << false << orig << part;
0129         QTest::newRow("part_to_orig") << false << part << orig;
0130         // Warnings: all tests without dirlister cache should above this line
0131         // and all tests with it should be below - the cache stays forever once it exists.
0132         QTest::newRow("orig_to_part_with_cache") << true << orig << part;
0133         QTest::newRow("part_to_orig_with_cache") << true << part << orig; // #218719
0134     }
0135 
0136     void testRename() // relies on testCopyToDesktop being run before
0137     {
0138         QFETCH(bool, withDirListerCache);
0139         QFETCH(QUrl, srcUrl);
0140         QFETCH(QUrl, destUrl);
0141 
0142         std::unique_ptr<KDirLister> lister;
0143         if (withDirListerCache) {
0144             lister.reset(new KDirLister);
0145             lister->openUrl(QUrl(QStringLiteral("desktop:/")));
0146             QSignalSpy spyCompleted(lister.get(), static_cast<void (KDirLister::*)()>(&KDirLister::completed));
0147             spyCompleted.wait();
0148         }
0149 
0150         org::kde::KDirNotify kdirnotify(QString(), QString(), QDBusConnection::sessionBus(), this);
0151         QSignalSpy spyFilesAdded(&kdirnotify, &org::kde::KDirNotify::FilesAdded);
0152         QSignalSpy spyFileRenamed(&kdirnotify, &org::kde::KDirNotify::FileRenamed);
0153         QSignalSpy spyFileRenamedWithLocalPath(&kdirnotify, &org::kde::KDirNotify::FileRenamedWithLocalPath);
0154 
0155         const QString srcFilePath(m_desktopPath + srcUrl.path());
0156         QVERIFY(QFile::exists(srcFilePath));
0157         const QString destFilePath(m_desktopPath + destUrl.path());
0158         QVERIFY(!QFile::exists(destFilePath));
0159 
0160         KIO::Job *job = KIO::rename(srcUrl, destUrl, KIO::HideProgressInfo);
0161         job->setUiDelegate(nullptr);
0162         QVERIFY(job);
0163         bool ok = job->exec();
0164         QVERIFY(ok);
0165         QVERIFY(!QFile::exists(srcFilePath));
0166         QVERIFY(QFile::exists(destFilePath));
0167 
0168         // kio_desktop's rename() calls org::kde::KDirNotify::emitFileRenamedWithLocalPath
0169         // FIXME: fix the double signal emission of fileRenamed
0170         // and then desktopnotifier notices something changed and emits KDirNotify::FilesAdded
0171         QTRY_VERIFY(spyFileRenamed.count() >= 1);
0172         QTRY_VERIFY(spyFileRenamedWithLocalPath.count() >= 1);
0173 
0174         // check that KDirLister now has the correct item (#382341)
0175         if (lister) {
0176             QTRY_COMPARE(lister->findByUrl(destUrl).targetUrl(), QUrl::fromLocalFile(destFilePath));
0177         }
0178     }
0179 
0180     void testTrashAndUndo()
0181     {
0182         // Given a file on the desktop...
0183         const QString localPath = m_desktopPath + '/' + m_testFileName;
0184         QVERIFY(QFile::exists(localPath));
0185 
0186         // ...moved to the trash
0187         const QUrl desktopUrl("desktop:/" + m_testFileName);
0188         KIO::Job *job = KIO::trash({desktopUrl}, KIO::HideProgressInfo);
0189         job->setUiDelegate(nullptr);
0190         KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, {desktopUrl}, QUrl(QStringLiteral("trash:/")), job);
0191         QVERIFY2(job->exec(), qPrintable(job->errorString()));
0192         QVERIFY(!QFile::exists(localPath));
0193 
0194         // When the user calls undo
0195         doUndo();
0196 
0197         // Then the file should re-appear
0198         QVERIFY(QFile::exists(localPath));
0199     }
0200 
0201 private:
0202     QString m_desktopPath;
0203     QString m_testFileName;
0204     QProcess m_kded;
0205 };
0206 
0207 QTEST_GUILESS_MAIN(TestDesktop)
0208 
0209 #include "kio_desktop_test.moc"