mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-26 06:55:59 +03:00
fd9b01981b
This has two positive effects: - We can put the error on the particular file that has missing data - We can sync all other files
168 lines
7.2 KiB
C++
168 lines
7.2 KiB
C++
/*
|
|
* This software is in the public domain, furnished "as is", without technical
|
|
* support, and with no warranty, express or implied, as to its usefulness for
|
|
* any purpose.
|
|
*
|
|
*/
|
|
|
|
#include <QtTest>
|
|
#include "syncenginetestutils.h"
|
|
#include <syncengine.h>
|
|
#include <localdiscoverytracker.h>
|
|
|
|
using namespace OCC;
|
|
|
|
SyncFileItemPtr findItem(const QSignalSpy &spy, const QString &path)
|
|
{
|
|
for (const QList<QVariant> &args : spy) {
|
|
auto item = args[0].value<SyncFileItemPtr>();
|
|
if (item->destination() == path)
|
|
return item;
|
|
}
|
|
return SyncFileItemPtr(new SyncFileItem);
|
|
}
|
|
|
|
struct FakeBrokenXmlPropfindReply : FakePropfindReply {
|
|
FakeBrokenXmlPropfindReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op,
|
|
const QNetworkRequest &request, QObject *parent)
|
|
: FakePropfindReply(remoteRootFileInfo, op, request, parent) {
|
|
QVERIFY(payload.size() > 50);
|
|
// turncate the XML
|
|
payload.chop(20);
|
|
}
|
|
};
|
|
|
|
struct MissingPermissionsPropfindReply : FakePropfindReply {
|
|
MissingPermissionsPropfindReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op,
|
|
const QNetworkRequest &request, QObject *parent)
|
|
: FakePropfindReply(remoteRootFileInfo, op, request, parent) {
|
|
// If the propfind contains a single file without permissions, this is a server error
|
|
const char toRemove[] = "<oc:permissions>RDNVCKW</oc:permissions>";
|
|
auto pos = payload.indexOf(toRemove, payload.size()/2);
|
|
QVERIFY(pos > 0);
|
|
payload.remove(pos, sizeof(toRemove) - 1);
|
|
}
|
|
};
|
|
|
|
|
|
enum ErrorKind : int {
|
|
// Lower code are corresponding to HTML error code
|
|
InvalidXML = 1000,
|
|
Timeout,
|
|
};
|
|
|
|
Q_DECLARE_METATYPE(ErrorCategory)
|
|
|
|
class TestRemoteDiscovery : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
|
|
void testRemoteDiscoveryError_data()
|
|
{
|
|
qRegisterMetaType<ErrorCategory>();
|
|
QTest::addColumn<int>("errorKind");
|
|
QTest::addColumn<QString>("expectedErrorString");
|
|
|
|
QString httpErrorMessage = "Server replied with an error while reading directory 'B' : Internal Server Fake Error";
|
|
|
|
QTest::newRow("404") << 404 << httpErrorMessage;
|
|
QTest::newRow("500") << 500 << httpErrorMessage;
|
|
QTest::newRow("503") << 503 << httpErrorMessage;
|
|
// 200 should be an error since propfind should return 207
|
|
QTest::newRow("200") << 200 << httpErrorMessage;
|
|
QTest::newRow("InvalidXML") << +InvalidXML << "error while reading directory 'B' : Unknown error";
|
|
QTest::newRow("Timeout") << +Timeout << "error while reading directory 'B' : Operation canceled";
|
|
}
|
|
|
|
|
|
// Check what happens when there is an error.
|
|
void testRemoteDiscoveryError()
|
|
{
|
|
QFETCH(int, errorKind);
|
|
QFETCH(QString, expectedErrorString);
|
|
bool syncSucceeds = errorKind == 503; // 503 just ignore the temporarily unavailable directory
|
|
|
|
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
|
|
|
// Do Some change as well
|
|
fakeFolder.localModifier().insert("A/z1");
|
|
fakeFolder.localModifier().insert("B/z1");
|
|
fakeFolder.localModifier().insert("C/z1");
|
|
fakeFolder.remoteModifier().insert("A/z2");
|
|
fakeFolder.remoteModifier().insert("B/z2");
|
|
fakeFolder.remoteModifier().insert("C/z2");
|
|
|
|
auto oldLocalState = fakeFolder.currentLocalState();
|
|
auto oldRemoteState = fakeFolder.currentRemoteState();
|
|
|
|
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *)
|
|
-> QNetworkReply *{
|
|
if (req.attribute(QNetworkRequest::CustomVerbAttribute) == "PROPFIND" && req.url().path().endsWith("/B")) {
|
|
if (errorKind == InvalidXML) {
|
|
return new FakeBrokenXmlPropfindReply(fakeFolder.remoteModifier(), op, req, this);
|
|
} else if (errorKind == Timeout) {
|
|
return new FakeHangingReply(op, req, this);
|
|
} else if (errorKind < 1000) {
|
|
return new FakeErrorReply(op, req, this, errorKind);
|
|
}
|
|
}
|
|
return nullptr;
|
|
});
|
|
|
|
// So the test that test timeout finishes fast
|
|
QScopedValueRollback<int> setHttpTimeout(AbstractNetworkJob::httpTimeout, errorKind == Timeout ? 1 : 10000);
|
|
|
|
QSignalSpy errorSpy(&fakeFolder.syncEngine(), &SyncEngine::syncError);
|
|
QCOMPARE(fakeFolder.syncOnce(), syncSucceeds);
|
|
qDebug() << "errorSpy=" << errorSpy;
|
|
|
|
// The folder B should not have been sync'ed (and in particular not removed)
|
|
QCOMPARE(oldLocalState.children["B"], fakeFolder.currentLocalState().children["B"]);
|
|
QCOMPARE(oldRemoteState.children["B"], fakeFolder.currentRemoteState().children["B"]);
|
|
if (!syncSucceeds) {
|
|
// Check we got the right error
|
|
QCOMPARE(errorSpy.count(), 1);
|
|
QVERIFY(errorSpy[0][0].toString().contains(expectedErrorString));
|
|
} else {
|
|
// The other folder should have been sync'ed as the sync just ignored the faulty dir
|
|
QCOMPARE(fakeFolder.currentRemoteState().children["A"], fakeFolder.currentLocalState().children["A"]);
|
|
QCOMPARE(fakeFolder.currentRemoteState().children["C"], fakeFolder.currentLocalState().children["C"]);
|
|
}
|
|
}
|
|
|
|
void testMissingData()
|
|
{
|
|
FakeFolder fakeFolder{ FileInfo() };
|
|
fakeFolder.remoteModifier().insert("good");
|
|
fakeFolder.remoteModifier().insert("noetag");
|
|
fakeFolder.remoteModifier().find("noetag")->etag.clear();
|
|
fakeFolder.remoteModifier().insert("nofileid");
|
|
fakeFolder.remoteModifier().find("nofileid")->fileId.clear();
|
|
fakeFolder.remoteModifier().mkdir("nopermissions");
|
|
fakeFolder.remoteModifier().insert("nopermissions/A");
|
|
|
|
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *)
|
|
-> QNetworkReply *{
|
|
if (req.attribute(QNetworkRequest::CustomVerbAttribute) == "PROPFIND" && req.url().path().endsWith("nopermissions"))
|
|
return new MissingPermissionsPropfindReply(fakeFolder.remoteModifier(), op, req, this);
|
|
return nullptr;
|
|
});
|
|
|
|
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
|
QVERIFY(!fakeFolder.syncOnce());
|
|
|
|
QCOMPARE(findItem(completeSpy, "good")->_instruction, CSYNC_INSTRUCTION_NEW);
|
|
QCOMPARE(findItem(completeSpy, "noetag")->_instruction, CSYNC_INSTRUCTION_ERROR);
|
|
QCOMPARE(findItem(completeSpy, "nofileid")->_instruction, CSYNC_INSTRUCTION_ERROR);
|
|
QCOMPARE(findItem(completeSpy, "nopermissions")->_instruction, CSYNC_INSTRUCTION_NEW);
|
|
QCOMPARE(findItem(completeSpy, "nopermissions/A")->_instruction, CSYNC_INSTRUCTION_ERROR);
|
|
QVERIFY(findItem(completeSpy, "noetag")->_errorString.contains("etag"));
|
|
QVERIFY(findItem(completeSpy, "nofileid")->_errorString.contains("file id"));
|
|
QVERIFY(findItem(completeSpy, "nopermissions/A")->_errorString.contains("permissions"));
|
|
}
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(TestRemoteDiscovery)
|
|
#include "testremotediscovery.moc"
|