Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorOlivier Goffart <ogoffart@woboq.com>2017-12-11 21:25:51 +0300
committerOlivier Goffart <olivier@woboq.com>2017-12-14 13:56:12 +0300
commit4dc49ff3b01f196dfac0ac928688d90a001b49e8 (patch)
tree7cf9995f7c23c88e70b1b11dd05e1ddfa54ca6c0 /test
parent4369853ddb21525a9b83f83ef3d44dd838a17d8d (diff)
SyncEngine: Recover when the PUT reply (or chunkin's MOVE) is lost
This can happen if the upload of a file is finished, but we just got disconnected right before recieving the reply containing the etag. So nothing was save din the DB, and we are not sure if the server recieved the file properly or not. Further local update of the file will cause a conflict. In order to fix this, store the checksum of the uploading file in the uploadinfo table of the local db (even if there is no chunking involved). And when we have a conflict, check that it is not because of this situation by checking the entry in the uploadinfo table. Issue #5106
Diffstat (limited to 'test')
-rw-r--r--test/syncenginetestutils.h13
-rw-r--r--test/testchunkingng.cpp70
2 files changed, 70 insertions, 13 deletions
diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h
index 9122040be..f7cf4eb46 100644
--- a/test/syncenginetestutils.h
+++ b/test/syncenginetestutils.h
@@ -452,7 +452,11 @@ public:
emit finished();
}
- void abort() override { }
+ void abort() override
+ {
+ setError(OperationCanceledError, "abort");
+ emit finished();
+ }
qint64 readData(char *, qint64) override { return 0; }
};
@@ -696,7 +700,12 @@ public:
emit finished();
}
- void abort() override { }
+ void abort() override
+ {
+ setError(OperationCanceledError, "abort");
+ emit finished();
+ }
+
qint64 readData(char *, qint64) override { return 0; }
};
diff --git a/test/testchunkingng.cpp b/test/testchunkingng.cpp
index a61bdc15d..ce7880537 100644
--- a/test/testchunkingng.cpp
+++ b/test/testchunkingng.cpp
@@ -161,17 +161,6 @@ private slots:
QVERIFY(!moveChecksumHeader.isEmpty());
fakeFolder.remoteModifier().find("A/a0")->checksums = moveChecksumHeader;
- // This time it's a real conflict, we have a remote checksum!
- connection = connect(&fakeFolder.syncEngine(), &SyncEngine::aboutToPropagate, [&](SyncFileItemVector &items) {
- SyncFileItemPtr a0;
- for (auto &item : items) {
- if (item->_file == "A/a0")
- a0 = item;
- }
-
- QVERIFY(a0);
- QCOMPARE(a0->_instruction, CSYNC_INSTRUCTION_CONFLICT);
- });
QVERIFY(fakeFolder.syncOnce());
disconnect(connection);
QCOMPARE(nGET, 0); // no new download, just a metadata update!
@@ -378,6 +367,65 @@ private slots:
QVERIFY(fakeFolder.uploadState().children.first().name != chunkingId);
}
+ // Check what happens when the connection is dropped on the PUT (non-chunking) or MOVE (chunking)
+ // for on the issue #5106
+ void connectionDroppedBeforeEtagRecieved_data()
+ {
+ QTest::addColumn<bool>("chunking");
+ QTest::newRow("big file") << true;
+ QTest::newRow("small file") << false;
+ }
+ void connectionDroppedBeforeEtagRecieved()
+ {
+ QFETCH(bool, chunking);
+ FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+ fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ { "chunking", "1.0" } } }, { "checksums", QVariantMap{ { "supportedTypes", QStringList() << "SHA1" } } } });
+ const int size = chunking ? 150 * 1000 * 1000 : 300;
+
+ // Make the MOVE never reply, but trigger a client-abort and apply the change remotely
+ QByteArray checksumHeader;
+ int nGET = 0;
+ QScopedValueRollback<int> setHttpTimeout(AbstractNetworkJob::httpTimeout, 1);
+ int responseDelay = AbstractNetworkJob::httpTimeout * 1000 * 1000; // much bigger than http timeout (so a timeout will occur)
+ // This will perform the operation on the server, but the reply will not come to the client
+ fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * {
+ if (!chunking && op == QNetworkAccessManager::PutOperation) {
+ checksumHeader = request.rawHeader("OC-Checksum");
+ return new DelayedReply<FakePutReply>(responseDelay, fakeFolder.remoteModifier(), op, request, outgoingData->readAll(), &fakeFolder.syncEngine());
+ } else if (chunking && request.attribute(QNetworkRequest::CustomVerbAttribute) == "MOVE") {
+ checksumHeader = request.rawHeader("OC-Checksum");
+ return new DelayedReply<FakeChunkMoveReply>(responseDelay, fakeFolder.uploadState(), fakeFolder.remoteModifier(), op, request, &fakeFolder.syncEngine());
+ } else if (op == QNetworkAccessManager::GetOperation) {
+ nGET++;
+ }
+ return nullptr;
+ });
+
+
+ // Test 1: a NEW file
+ fakeFolder.localModifier().insert("A/a0", size);
+ QVERIFY(!fakeFolder.syncOnce()); // timeout!
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); // but the upload succeeded
+ QVERIFY(!checksumHeader.isEmpty());
+ fakeFolder.remoteModifier().find("A/a0")->checksums = checksumHeader; // The test system don't do that automatically
+ // Should be resolved properly
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ // Test 2: Modify the file further
+ fakeFolder.localModifier().appendByte("A/a0");
+ QVERIFY(!fakeFolder.syncOnce()); // timeout!
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); // but the upload succeeded
+ fakeFolder.remoteModifier().find("A/a0")->checksums = checksumHeader;
+ // modify again, should not cause conflict
+ fakeFolder.localModifier().appendByte("A/a0");
+ QVERIFY(!fakeFolder.syncOnce()); // now it's trying to upload the modified file
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+ fakeFolder.remoteModifier().find("A/a0")->checksums = checksumHeader;
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
+ }
};
QTEST_GUILESS_MAIN(TestChunkingNG)