mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-21 20:45:51 +03:00
Test that the sync behave well if there are errors while reading the database
This commit is contained in:
parent
b79e57d1c1
commit
bb9ce8db21
5 changed files with 108 additions and 5 deletions
|
@ -279,6 +279,13 @@ bool SyncJournalDb::sqlFail(const QString &log, const SqlQuery &query)
|
|||
|
||||
bool SyncJournalDb::checkConnect()
|
||||
{
|
||||
if (autotestFailCounter >= 0) {
|
||||
if (!autotestFailCounter--) {
|
||||
qCInfo(lcDb) << "Error Simulated";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_db.isOpen()) {
|
||||
// Unfortunately the sqlite isOpen check can return true even when the underlying storage
|
||||
// has become unavailable - and then some operations may cause crashes. See #6049
|
||||
|
@ -634,7 +641,7 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
|||
bool re = true;
|
||||
|
||||
// check if the file_id column is there and create it if not
|
||||
if (!checkConnect()) {
|
||||
if (columns.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -751,7 +758,10 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
|||
commitInternal("update database structure: add isE2eEncrypted col");
|
||||
}
|
||||
|
||||
if (!tableColumns("uploadinfo").contains("contentChecksum")) {
|
||||
auto uploadInfoColumns = tableColumns("uploadinfo");
|
||||
if (uploadInfoColumns.isEmpty())
|
||||
return false;
|
||||
if (!uploadInfoColumns.contains("contentChecksum")) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE uploadinfo ADD COLUMN contentChecksum TEXT;");
|
||||
if (!query.exec()) {
|
||||
|
@ -761,7 +771,10 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
|||
commitInternal("update database structure: add contentChecksum col for uploadinfo");
|
||||
}
|
||||
|
||||
if (!tableColumns("conflicts").contains("basePath")) {
|
||||
auto conflictsColumns = tableColumns("conflicts");
|
||||
if (conflictsColumns.isEmpty())
|
||||
return false;
|
||||
if (!conflictsColumns.contains("basePath")) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE conflicts ADD COLUMN basePath TEXT;");
|
||||
if (!query.exec()) {
|
||||
|
@ -788,8 +801,7 @@ bool SyncJournalDb::updateErrorBlacklistTableStructure()
|
|||
auto columns = tableColumns("blacklist");
|
||||
bool re = true;
|
||||
|
||||
// check if the file_id column is there and create it if not
|
||||
if (!checkConnect()) {
|
||||
if (columns.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -245,6 +245,13 @@ public:
|
|||
*/
|
||||
void markVirtualFileForDownloadRecursively(const QByteArray &path);
|
||||
|
||||
/**
|
||||
* Only used for auto-test:
|
||||
* when positive, will decrease the counter for every database operation.
|
||||
* reaching 0 makes the operation fails
|
||||
*/
|
||||
int autotestFailCounter = -1;
|
||||
|
||||
private:
|
||||
int getFileRecordCount();
|
||||
bool updateDatabaseStructure();
|
||||
|
|
|
@ -58,6 +58,7 @@ nextcloud_add_test(LocalDiscovery "syncenginetestutils.h")
|
|||
nextcloud_add_test(RemoteDiscovery "syncenginetestutils.h")
|
||||
nextcloud_add_test(Permissions "syncenginetestutils.h")
|
||||
nextcloud_add_test(SelectiveSync "syncenginetestutils.h")
|
||||
nextcloud_add_test(DatabaseError "syncenginetestutils.h")
|
||||
nextcloud_add_test(LockedFiles "syncenginetestutils.h;../src/gui/lockwatcher.cpp")
|
||||
nextcloud_add_test(FolderWatcher "${FolderWatcher_SRC}")
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "filesystem.h"
|
||||
#include "syncengine.h"
|
||||
#include "common/syncjournaldb.h"
|
||||
#include "csync_exclude.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QNetworkReply>
|
||||
|
@ -920,6 +921,8 @@ public:
|
|||
|
||||
_journalDb = std::make_unique<OCC::SyncJournalDb>(localPath() + "._sync_test.db");
|
||||
_syncEngine = std::make_unique<OCC::SyncEngine>(_account, localPath(), "", _journalDb.get());
|
||||
// Ignore temporary files from the download. (This is in the default exclude list, but we don't load it)
|
||||
_syncEngine->excludedFiles().addManualExclude("]*.~*");
|
||||
|
||||
// A new folder will update the local file state database on first sync.
|
||||
// To have a state matching what users will encounter, we have to a sync
|
||||
|
|
80
test/testdatabaseerror.cpp
Normal file
80
test/testdatabaseerror.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
|
||||
class TestDatabaseError : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void testDatabaseError() {
|
||||
/* This test will make many iteration, at each iteration, the iᵗʰ database access will fail.
|
||||
* The test ensure that if there is a failure, the next sync recovers. And if there was
|
||||
* no error, then everything was sync'ed properly.
|
||||
*/
|
||||
|
||||
FileInfo finalState;
|
||||
for (int count = 0; true; ++count) {
|
||||
qInfo() << "Starting Iteration" << count;
|
||||
|
||||
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
||||
|
||||
// Do a couple of changes
|
||||
fakeFolder.remoteModifier().insert("A/a0");
|
||||
fakeFolder.remoteModifier().appendByte("A/a1");
|
||||
fakeFolder.remoteModifier().remove("A/a2");
|
||||
fakeFolder.remoteModifier().rename("S/s1", "S/s1_renamed");
|
||||
fakeFolder.remoteModifier().mkdir("D");
|
||||
fakeFolder.remoteModifier().mkdir("D/subdir");
|
||||
fakeFolder.remoteModifier().insert("D/subdir/file");
|
||||
fakeFolder.localModifier().insert("B/b0");
|
||||
fakeFolder.localModifier().appendByte("B/b1");
|
||||
fakeFolder.remoteModifier().remove("B/b2");
|
||||
fakeFolder.localModifier().mkdir("NewDir");
|
||||
fakeFolder.localModifier().rename("C", "NewDir/C");
|
||||
|
||||
// Set the counter
|
||||
fakeFolder.syncJournal().autotestFailCounter = count;
|
||||
|
||||
// run the sync
|
||||
bool result = fakeFolder.syncOnce();
|
||||
|
||||
qInfo() << "Result of iteration" << count << "was" << result;
|
||||
|
||||
if (fakeFolder.syncJournal().autotestFailCounter >= 0) {
|
||||
// No error was thrown, we are finished
|
||||
QVERIFY(result);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QCOMPARE(fakeFolder.currentRemoteState(), finalState);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
fakeFolder.syncJournal().autotestFailCounter = -1;
|
||||
// Try again
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
}
|
||||
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
if (count == 0) {
|
||||
finalState = fakeFolder.currentRemoteState();
|
||||
} else {
|
||||
// the final state should be the same for every iteration
|
||||
QCOMPARE(fakeFolder.currentRemoteState(), finalState);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestDatabaseError)
|
||||
#include "testdatabaseerror.moc"
|
Loading…
Reference in a new issue