1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
/*
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
*/
#include <utility>
#include <QProcess>
#include <QTest>
#include <KIO/Job>
#include "transfer_resume.h"
struct FakeWorker {
struct FakeError {
int id;
QString message;
};
void error(int id, const QString &message)
{
m_errors.push_back(FakeError{id, message});
}
bool configValue(const QString &key, bool defaultValue) const
{
return m_config.value(key, defaultValue);
}
bool canResume(KIO::filesize_t offset)
{
Q_UNUSED(offset); // in reality this is a user query, always assume the user says yes for now
return true;
}
void debugErrors()
{
for (const auto &error : std::as_const(m_errors)) {
qDebug() << "ERROR{" << KIO::buildErrorString(error.id, error.message) << "}";
}
}
QList<FakeError> m_errors;
QHash<QString, bool> m_config = {{"MarkPartial", true}};
};
class ShouldResumeTest : public QObject
{
Q_OBJECT
private:
QTemporaryDir m_tmpDir;
private Q_SLOTS:
void initTestCase()
{
QVERIFY(m_tmpDir.isValid());
const QString fixturesPath = QFINDTESTDATA("fixtures/.");
QProcess proc;
proc.setProcessChannelMode(QProcess::ForwardedChannels);
proc.start("cp", {"-rv", fixturesPath, m_tmpDir.path()});
QVERIFY(proc.waitForFinished());
QCOMPARE(proc.exitCode(), 0);
}
QString tmpPath(const QString &subpath)
{
return QDir(m_tmpDir.path()).filePath(subpath);
}
QUrl tmpUrl(const QString &subpath)
{
return QUrl::fromLocalFile(tmpPath(subpath));
}
void noResumeButPartial()
{
// NB: this has no fixture ;)
FakeWorker worker;
auto url = tmpUrl("noResumeButPartial/thing");
auto partUrl = tmpUrl("noResumeButPartial/thing.part");
auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
auto resume = std::get<TransferContext>(resumeVariant);
QCOMPARE(resume.resuming, false);
QCOMPARE(resume.destination, partUrl);
QCOMPARE(resume.completeDestination, url);
QCOMPARE(resume.partDestination, partUrl);
QDir().mkdir(tmpPath("noResumeButPartial"));
QFile part(partUrl.toLocalFile());
QVERIFY(part.open(QFile::WriteOnly));
part.write("");
QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
QVERIFY(QFileInfo::exists(url.toLocalFile()));
QVERIFY(!QFileInfo::exists(partUrl.toLocalFile()));
}
void noResumeAndNoPartial()
{
// NB: this has no fixture ;)
FakeWorker worker;
worker.m_config["MarkPartial"] = false;
auto url = tmpUrl("noResumeAndNoPartial/thing");
auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
worker.debugErrors();
QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
auto resume = std::get<TransferContext>(resumeVariant);
QCOMPARE(resume.resuming, false);
QCOMPARE(resume.destination, url);
QCOMPARE(resume.completeDestination, url);
QCOMPARE(resume.partDestination, QUrl());
QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
}
void resume()
{
FakeWorker worker;
auto url = tmpUrl("resume/thing");
auto partUrl = tmpUrl("resume/thing.part");
auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
auto resume = std::get<TransferContext>(resumeVariant);
QCOMPARE(resume.resuming, true);
QCOMPARE(resume.destination, partUrl);
QCOMPARE(resume.completeDestination, url);
QCOMPARE(resume.partDestination, partUrl);
QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
QVERIFY(QFileInfo::exists(url.toLocalFile()));
QVERIFY(!QFileInfo::exists(partUrl.toLocalFile()));
}
void resumeInPlace()
{
FakeWorker worker;
auto url = tmpUrl("resumeInPlace/thing");
auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::Resume, &worker);
QVERIFY(std::holds_alternative<TransferContext>(resumeVariant));
auto resume = std::get<TransferContext>(resumeVariant);
QCOMPARE(resume.resuming, true);
QCOMPARE(resume.destination, url);
QCOMPARE(resume.completeDestination, url);
QCOMPARE(resume.partDestination, url);
QVERIFY(Transfer::concludeResumeHasError<QFileResumeIO>(WorkerResult::pass(), resume, &worker).success());
QVERIFY(QFileInfo::exists(url.toLocalFile()));
}
void noResumeInPlace()
{
FakeWorker worker;
auto url = tmpUrl("resumeInPlace/thing"); // intentionally the same path this scenario errors out
auto resumeVariant = Transfer::shouldResume<QFileResumeIO>(url, KIO::JobFlags(), &worker);
QVERIFY(std::holds_alternative<WorkerResult>(resumeVariant));
auto result = std::get<WorkerResult>(resumeVariant);
QCOMPARE(result.error(), KIO::ERR_FILE_ALREADY_EXIST);
}
};
QTEST_GUILESS_MAIN(ShouldResumeTest)
#include "shouldresumetest.moc"
|