const FileInfo *fileInfo;
char payload;
int size;
+ bool aborted = false;
FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
: QNetworkReply{parent} {
}
Q_INVOKABLE void respond() {
+ if (aborted) {
+ setError(OperationCanceledError, "Operation Canceled");
+ emit metaDataChanged();
+ emit finished();
+ return;
+ }
payload = fileInfo->contentChar;
size = fileInfo->size;
setHeader(QNetworkRequest::ContentLengthHeader, size);
emit finished();
}
- void abort() override { }
- qint64 bytesAvailable() const override { return size + QIODevice::bytesAvailable(); }
+ void abort() override {
+ aborted = true;
+ }
+ qint64 bytesAvailable() const override {
+ if (aborted)
+ return 0;
+ return size + QIODevice::bytesAvailable();
+ }
qint64 readData(char *data, qint64 maxlen) override {
qint64 len = std::min(qint64{size}, maxlen);
{
Q_OBJECT
public:
- FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
- : QNetworkReply{parent} {
+ FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
+ QObject *parent, int httpErrorCode)
+ : QNetworkReply{parent}, _httpErrorCode(httpErrorCode) {
setRequest(request);
setUrl(request.url());
setOperation(op);
}
Q_INVOKABLE void respond() {
- setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 500);
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute, _httpErrorCode);
setError(InternalServerError, "Internal Server Fake Error");
emit metaDataChanged();
emit finished();
void abort() override { }
qint64 readData(char *, qint64) override { return 0; }
+
+ int _httpErrorCode;
};
class FakeQNAM : public QNetworkAccessManager
{
FileInfo _remoteRootFileInfo;
FileInfo _uploadFileInfo;
- QStringList _errorPaths;
+ // maps a path to an HTTP error
+ QHash<QString, int> _errorPaths;
public:
FakeQNAM(FileInfo initialRoot) : _remoteRootFileInfo{std::move(initialRoot)} { }
FileInfo ¤tRemoteState() { return _remoteRootFileInfo; }
FileInfo &uploadState() { return _uploadFileInfo; }
- QStringList &errorPaths() { return _errorPaths; }
+
+ QHash<QString, int> &errorPaths() { return _errorPaths; }
protected:
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
const QString fileName = getFilePathFromUrl(request.url());
Q_ASSERT(!fileName.isNull());
if (_errorPaths.contains(fileName))
- return new FakeErrorReply{op, request, this};
+ return new FakeErrorReply{op, request, this, _errorPaths[fileName]};
bool isUpload = request.url().path().startsWith(sUploadUrl.path());
FileInfo &info = isUpload ? _uploadFileInfo : _remoteRootFileInfo;
FileInfo currentRemoteState() { return _fakeQnam->currentRemoteState(); }
FileInfo &uploadState() { return _fakeQnam->uploadState(); }
- QStringList &serverErrorPaths() { return _fakeQnam->errorPaths(); }
+ struct ErrorList {
+ FakeQNAM *_qnam;
+ void append(const QString &path, int error = 500)
+ { _qnam->errorPaths().insert(path, error); }
+ void clear() { _qnam->errorPaths().clear(); }
+ };
+ ErrorList serverErrorPaths() { return {_fakeQnam}; }
QString localPath() const {
// SyncEngine wants a trailing slash
QCOMPARE(finishedSpy.size(), 1);
QCOMPARE(finishedSpy.first().first().toBool(), false);
}
+
+ void testDirDownloadWithError() {
+ FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
+ QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
+ fakeFolder.remoteModifier().mkdir("Y");
+ fakeFolder.remoteModifier().mkdir("Y/Z");
+ fakeFolder.remoteModifier().insert("Y/Z/d0");
+ fakeFolder.remoteModifier().insert("Y/Z/d1");
+ fakeFolder.remoteModifier().insert("Y/Z/d2");
+ fakeFolder.remoteModifier().insert("Y/Z/d3");
+ fakeFolder.remoteModifier().insert("Y/Z/d4");
+ fakeFolder.remoteModifier().insert("Y/Z/d5");
+ fakeFolder.remoteModifier().insert("Y/Z/d6");
+ fakeFolder.remoteModifier().insert("Y/Z/d7");
+ fakeFolder.remoteModifier().insert("Y/Z/d8");
+ fakeFolder.remoteModifier().insert("Y/Z/d9");
+ fakeFolder.serverErrorPaths().append("Y/Z/d2", 503); // 503 is a fatal error
+ fakeFolder.serverErrorPaths().append("Y/Z/d3", 503); // 503 is a fatal error
+ QVERIFY(!fakeFolder.syncOnce());
+ QCoreApplication::processEvents(); // should not crash
+
+ QSet<QString> seen;
+ for(const QList<QVariant> &args : completeSpy) {
+ auto item = args[0].value<SyncFileItemPtr>();
+ qDebug() << item->_file << item->_isDirectory << item->_status;
+ QVERIFY(!seen.contains(item->_file)); // signal only sent once per item
+ seen.insert(item->_file);
+ if (item->_file == "Y/Z/d2" || item->_file == "Y/Z/d3") {
+ QVERIFY(item->_status == SyncFileItem::FatalError);
+ }
+ QVERIFY(item->_file != "Y/Z/d9"); // we should have aborted the sync before d9 starts
+ }
+ }
+
};
QTEST_GUILESS_MAIN(TestSyncEngine)