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"