File indexing completed on 2024-05-12 04:59:53

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003     SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0004 */
0005 
0006 #include <utility>
0007 
0008 #include <QProcess>
0009 #include <QTest>
0010 
0011 #include <KIO/Job>
0012 
0013 #include "transfer_resume.h"
0014 
0015 struct FakeWorker {
0016     struct FakeError {
0017         int id;
0018         QString message;
0019     };
0020 
0021     void error(int id, const QString &message)
0022     {
0023         m_errors.push_back(FakeError{id, message});
0024     }
0025 
0026     bool configValue(const QString &key, bool defaultValue) const
0027     {
0028         return m_config.value(key, defaultValue);
0029     }
0030 
0031     bool canResume(KIO::filesize_t offset)
0032     {
0033         Q_UNUSED(offset); // in reality this is a user query, always assume the user says yes for now
0034         return true;
0035     }
0036 
0037     void debugErrors()
0038     {
0039         for (const auto &error : std::as_const(m_errors)) {
0040             qDebug() << "ERROR{" << KIO::buildErrorString(error.id, error.message) << "}";
0041         }
0042     }
0043 
0044     QVector<FakeError> m_errors;
0045     QHash<QString, bool> m_config = {{"MarkPartial", true}};
0046 };
0047 
0048 class ShouldResumeTest : public QObject
0049 {
0050     Q_OBJECT
0051 private:
0052     QTemporaryDir m_tmpDir;
0053 private Q_SLOTS:
0054     void initTestCase()
0055     {
0056         QVERIFY(m_tmpDir.isValid());
0057         const QString fixturesPath = QFINDTESTDATA("fixtures/.");
0058         QProcess proc;
0059         proc.setProcessChannelMode(QProcess::ForwardedChannels);
0060         proc.start("cp", {"-rv", fixturesPath, m_tmpDir.path()});
0061         QVERIFY(proc.waitForFinished());
0062         QCOMPARE(proc.exitCode(), 0);
0063     }
0064 
0065     QString tmpPath(const QString &subpath)
0066     {
0067         return QDir(m_tmpDir.path()).filePath(subpath);
0068     }
0069 
0070     QUrl tmpUrl(const QString &subpath)
0071     {
0072         return QUrl::fromLocalFile(tmpPath(subpath));
0073     }
0074 
0075     void noResumeButPartial()
0076     {
0077         // NB: this has no fixture ;)
0078         FakeWorker worker;
0079 
0080         auto url = tmpUrl("noResumeButPartial/thing");
0081         auto partUrl = tmpUrl("noResumeButPartial/thing.part");
0082         auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
0083         QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
0084         auto resume = std::get<TransferContext>(resumeVariant);
0085         QCOMPARE(resume.resuming, false);
0086         QCOMPARE(resume.destination, partUrl);
0087         QCOMPARE(resume.completeDestination, url);
0088         QCOMPARE(resume.partDestination, partUrl);
0089 
0090         QDir().mkdir(tmpPath("noResumeButPartial"));
0091         QFile part(partUrl.toLocalFile());
0092         QVERIFY(part.open(QFile::WriteOnly));
0093         part.write("");
0094         QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
0095         QVERIFY(QFileInfo::exists(url.toLocalFile()));
0096         QVERIFY(!QFileInfo::exists(partUrl.toLocalFile()));
0097     }
0098 
0099     void noResumeAndNoPartial()
0100     {
0101         // NB: this has no fixture ;)
0102         FakeWorker worker;
0103         worker.m_config["MarkPartial"] = false;
0104 
0105         auto url = tmpUrl("noResumeAndNoPartial/thing");
0106         auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
0107         worker.debugErrors();
0108         QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
0109         auto resume = std::get<TransferContext>(resumeVariant);
0110         QCOMPARE(resume.resuming, false);
0111         QCOMPARE(resume.destination, url);
0112         QCOMPARE(resume.completeDestination, url);
0113         QCOMPARE(resume.partDestination, QUrl());
0114         QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
0115     }
0116 
0117     void resume()
0118     {
0119         FakeWorker worker;
0120 
0121         auto url = tmpUrl("resume/thing");
0122         auto partUrl = tmpUrl("resume/thing.part");
0123         auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
0124         QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
0125         auto resume = std::get<TransferContext>(resumeVariant);
0126         QCOMPARE(resume.resuming, true);
0127         QCOMPARE(resume.destination, partUrl);
0128         QCOMPARE(resume.completeDestination, url);
0129         QCOMPARE(resume.partDestination, partUrl);
0130 
0131         QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
0132         QVERIFY(QFileInfo::exists(url.toLocalFile()));
0133         QVERIFY(!QFileInfo::exists(partUrl.toLocalFile()));
0134     }
0135 
0136     void resumeInPlace()
0137     {
0138         FakeWorker worker;
0139 
0140         auto url = tmpUrl("resumeInPlace/thing");
0141         auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::Resume, &worker);
0142         QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
0143         auto resume = std::get<TransferContext>(resumeVariant);
0144         QCOMPARE(resume.resuming, true);
0145         QCOMPARE(resume.destination, url);
0146         QCOMPARE(resume.completeDestination, url);
0147         QCOMPARE(resume.partDestination, url);
0148 
0149         QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
0150         QVERIFY(QFileInfo::exists(url.toLocalFile()));
0151     }
0152 
0153     void noResumeInPlace()
0154     {
0155         FakeWorker worker;
0156 
0157         auto url = tmpUrl("resumeInPlace/thing"); // intentionally the same path this scenario errors out
0158         auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
0159         QVERIFY(std::holds_alternative<WorkerResult>(resumeVariant));
0160         auto result = std::get<WorkerResult>(resumeVariant);
0161         QCOMPARE(result.error(), KIO::ERR_FILE_ALREADY_EXIST);
0162     }
0163 };
0164 
0165 QTEST_GUILESS_MAIN(ShouldResumeTest)
0166 
0167 #include "shouldresumetest.moc"