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
diff options
context:
space:
mode:
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>2022-03-03 12:53:50 +0300
committerMatthieu Gallien (Rebase PR Action) <matthieu_gallien@yahoo.fr>2022-03-18 02:28:02 +0300
commitd4081c99a7916ec0641789530f287137abebc140 (patch)
tree68c6c422540549e412dcde27e4fb7a7714e97dea
parentb8ff1525f78e9c6ba065001cff9106a91db6936b (diff)
prevent updating files when that would result in invalid mtime
ensure that we never try to sync a file with a sync that would result in setting it up to an invalid mtime test a recovery scenario with plain old sync folder and virtual files Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
-rw-r--r--src/libsync/discovery.cpp20
-rw-r--r--test/syncenginetestutils.cpp7
-rw-r--r--test/syncenginetestutils.h2
-rw-r--r--test/testlocaldiscovery.cpp194
-rw-r--r--test/testsyncengine.cpp28
-rw-r--r--test/testsyncvirtualfiles.cpp65
6 files changed, 280 insertions, 36 deletions
diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp
index c9a3b1d53..cb6ce2755 100644
--- a/src/libsync/discovery.cpp
+++ b/src/libsync/discovery.cpp
@@ -848,6 +848,12 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
if (_queryLocal != NormalQuery && _queryServer != NormalQuery)
recurse = false;
+ if ((item->_direction == SyncFileItem::Down || item->_instruction == CSYNC_INSTRUCTION_CONFLICT) && (item->_modtime <= 0 || item->_modtime >= 0xFFFFFFFF)) {
+ item->_instruction = CSYNC_INSTRUCTION_ERROR;
+ item->_errorString = tr("Cannot sync due to invalid modification time");
+ item->_status = SyncFileItem::Status::NormalError;
+ }
+
auto recurseQueryLocal = _queryLocal == ParentNotChanged ? ParentNotChanged : localEntry.isDirectory || item->_instruction == CSYNC_INSTRUCTION_RENAME ? NormalQuery : ParentDontExist;
processFileFinalize(item, path, recurse, recurseQueryLocal, recurseQueryServer);
};
@@ -875,14 +881,14 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
}
path._original = originalPath;
item->_originalFile = path._original;
- item->_modtime = base._modtime;
- item->_inode = base._inode;
+ item->_modtime = base.isValid() ? base._modtime : localEntry.modtime;
+ item->_inode = base.isValid() ? base._inode : localEntry.inode;
item->_instruction = CSYNC_INSTRUCTION_RENAME;
item->_direction = direction;
- item->_fileId = base._fileId;
- item->_remotePerm = base._remotePerm;
- item->_etag = base._etag;
- item->_type = base._type;
+ item->_fileId = base.isValid() ? base._fileId : QByteArray{};
+ item->_remotePerm = base.isValid() ? base._remotePerm : RemotePermissions{};
+ item->_etag = base.isValid() ? base._etag : QByteArray{};
+ item->_type = base.isValid() ? base._type : localEntry.type;
};
if (!localEntry.isValid()) {
@@ -1002,7 +1008,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo(
item->_type = localEntry.isDirectory ? ItemTypeDirectory : ItemTypeFile;
_childModified = true;
} else if (dbEntry._modtime > 0 && localEntry.modtime <= 0) {
- item->_instruction = CSYNC_INSTRUCTION_SYNC;
+ item->_instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
item->_direction = SyncFileItem::Down;
item->_size = localEntry.size > 0 ? localEntry.size : dbEntry._fileSize;
item->_modtime = dbEntry._modtime;
diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp
index a5eaaaaf7..1d813e3ff 100644
--- a/test/syncenginetestutils.cpp
+++ b/test/syncenginetestutils.cpp
@@ -188,6 +188,13 @@ void FileInfo::setModTime(const QString &relativePath, const QDateTime &modTime)
file->lastModified = modTime;
}
+void FileInfo::setModTimeKeepEtag(const QString &relativePath, const QDateTime &modTime)
+{
+ FileInfo *file = find(relativePath);
+ Q_ASSERT(file);
+ file->lastModified = modTime;
+}
+
FileInfo *FileInfo::find(PathComponents pathComponents, const bool invalidateEtags)
{
if (pathComponents.isEmpty()) {
diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h
index c434925cc..8abe50848 100644
--- a/test/syncenginetestutils.h
+++ b/test/syncenginetestutils.h
@@ -128,6 +128,8 @@ public:
void setModTime(const QString &relativePath, const QDateTime &modTime) override;
+ void setModTimeKeepEtag(const QString &relativePath, const QDateTime &modTime);
+
FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false);
FileInfo *createDir(const QString &relativePath);
diff --git a/test/testlocaldiscovery.cpp b/test/testlocaldiscovery.cpp
index 448d2d2b6..fa714129d 100644
--- a/test/testlocaldiscovery.cpp
+++ b/test/testlocaldiscovery.cpp
@@ -387,8 +387,11 @@ private slots:
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
}
- void testInvalidMtimeRecovery()
+ void testBlockInvalidMtimeSyncRemote()
{
+ constexpr auto INVALID_MODTIME1 = 0;
+ constexpr auto INVALID_MODTIME2 = -3600;
+
FakeFolder fakeFolder{FileInfo{}};
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
const QString fooFileRootFolder("foo");
@@ -408,44 +411,177 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
- fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
- fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(0));
+ fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+
+ QVERIFY(!fakeFolder.syncOnce());
+
+ QVERIFY(!fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+
+ QVERIFY(!fakeFolder.syncOnce());
+
+ QVERIFY(!fakeFolder.syncOnce());
+ }
+
+ void testBlockInvalidMtimeSyncLocal()
+ {
+ constexpr auto INVALID_MODTIME1 = 0;
+ constexpr auto INVALID_MODTIME2 = -3600;
+
+ FakeFolder fakeFolder{FileInfo{}};
+
+ int nGET = 0;
+ fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &, QIODevice *) {
+ if (op == QNetworkAccessManager::GetOperation)
+ ++nGET;
+ return nullptr;
+ });
+
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+ const QString fooFileRootFolder("foo");
+ const QString barFileRootFolder("bar");
+ const QString blaFileRootFolder("bla");
+ const QString fooFileSubFolder("subfolder/foo");
+ const QString barFileSubFolder("subfolder/bar");
+ const QString blaFileSubFolder("subfolder/bla");
+
+ fakeFolder.remoteModifier().insert(fooFileRootFolder);
+ fakeFolder.remoteModifier().insert(barFileRootFolder);
+ fakeFolder.remoteModifier().insert(blaFileRootFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileSubFolder);
+ fakeFolder.remoteModifier().insert(barFileSubFolder);
+ fakeFolder.remoteModifier().insert(blaFileSubFolder);
QVERIFY(fakeFolder.syncOnce());
+ nGET = 0;
+
+ fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
+ fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME1));
QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
- auto expectedState = fakeFolder.currentLocalState();
- QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
- fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.remoteModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.remoteModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
- fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(-3600));
+ fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.localModifier().setModTime(blaFileRootFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
+ fakeFolder.localModifier().setModTime(blaFileSubFolder, QDateTime::fromSecsSinceEpoch(INVALID_MODTIME2));
- QVERIFY(!fakeFolder.syncOnce());
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
+
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(nGET, 0);
+ }
+
+ void testDoNotSyncInvalidFutureMtime()
+ {
+ constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
+ constexpr auto CURRENT_MTIME = 1646057277;
+
+ FakeFolder fakeFolder{FileInfo{}};
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ const QString fooFileRootFolder("foo");
+ const QString barFileRootFolder("bar");
+ const QString fooFileSubFolder("subfolder/foo");
+ const QString barFileSubFolder("subfolder/bar");
+ const QString fooFileAaaSubFolder("aaa/subfolder/foo");
+ const QString barFileAaaSubFolder("aaa/subfolder/bar");
+
+ fakeFolder.remoteModifier().insert(fooFileRootFolder);
+ fakeFolder.remoteModifier().insert(barFileRootFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileSubFolder);
+ fakeFolder.remoteModifier().insert(barFileSubFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
+ fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.remoteModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.remoteModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.remoteModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.remoteModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.remoteModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
QVERIFY(!fakeFolder.syncOnce());
+ }
+
+ void testInvalidFutureMtimeRecovery()
+ {
+ constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
+ constexpr auto CURRENT_MTIME = 1646057277;
+
+ FakeFolder fakeFolder{FileInfo{}};
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- expectedState = fakeFolder.currentLocalState();
+ const QString fooFileRootFolder("foo");
+ const QString barFileRootFolder("bar");
+ const QString fooFileSubFolder("subfolder/foo");
+ const QString barFileSubFolder("subfolder/bar");
+ const QString fooFileAaaSubFolder("aaa/subfolder/foo");
+ const QString barFileAaaSubFolder("aaa/subfolder/bar");
+
+ fakeFolder.remoteModifier().insert(fooFileRootFolder);
+ fakeFolder.remoteModifier().insert(barFileRootFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileSubFolder);
+ fakeFolder.remoteModifier().insert(barFileSubFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
+ fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileRootFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ auto expectedState = fakeFolder.currentLocalState();
QCOMPARE(fakeFolder.currentRemoteState(), expectedState);
}
};
diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp
index 1ec461aa1..bc6125616 100644
--- a/test/testsyncengine.cpp
+++ b/test/testsyncengine.cpp
@@ -1129,6 +1129,34 @@ private slots:
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
}
+
+ void testFolderWithFilesInError()
+ {
+ FakeFolder fakeFolder{FileInfo{}};
+
+ fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData) -> QNetworkReply * {
+ Q_UNUSED(outgoingData)
+
+ if (op == QNetworkAccessManager::GetOperation) {
+ const auto fileName = getFilePathFromUrl(request.url());
+ if (fileName == QStringLiteral("aaa/subfolder/foo")) {
+ return new FakeErrorReply(op, request, &fakeFolder.syncEngine(), 403);
+ }
+ }
+ return nullptr;
+ });
+
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(QStringLiteral("aaa/subfolder/bar"));
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().insert(QStringLiteral("aaa/subfolder/foo"));
+ QVERIFY(!fakeFolder.syncOnce());
+
+ QVERIFY(!fakeFolder.syncOnce());
+ }
};
QTEST_GUILESS_MAIN(TestSyncEngine)
diff --git a/test/testsyncvirtualfiles.cpp b/test/testsyncvirtualfiles.cpp
index 882879c7b..e45fc7d58 100644
--- a/test/testsyncvirtualfiles.cpp
+++ b/test/testsyncvirtualfiles.cpp
@@ -1517,6 +1517,71 @@ private slots:
QCOMPARE(fakeFolder.currentLocalState().find("A/hello" DVSUFFIX)->size, 222);
QCOMPARE(fakeFolder.currentLocalState().find("A/igno" DVSUFFIX)->size, 123);
}
+
+ void testUpdateMetadataErrorManagement()
+ {
+ FakeFolder fakeFolder{FileInfo{}};
+ setupVfs(fakeFolder);
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ // Existing files are propagated just fine in both directions
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(QStringLiteral("aaa/subfolder/bar"));
+ QVERIFY(fakeFolder.syncOnce());
+
+ // New files on the remote create virtual files
+ fakeFolder.remoteModifier().setModTime(QStringLiteral("aaa/subfolder/bar"), QDateTime::fromSecsSinceEpoch(0));
+ QVERIFY(!fakeFolder.syncOnce());
+
+ QVERIFY(!fakeFolder.syncOnce());
+ }
+
+ void testInvalidFutureMtimeRecovery()
+ {
+ constexpr auto FUTURE_MTIME = 0xFFFFFFFF;
+ constexpr auto CURRENT_MTIME = 1646057277;
+
+ FakeFolder fakeFolder{FileInfo{}};
+ setupVfs(fakeFolder);
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ const QString fooFileRootFolder("foo");
+ const QString barFileRootFolder("bar");
+ const QString fooFileSubFolder("subfolder/foo");
+ const QString barFileSubFolder("subfolder/bar");
+ const QString fooFileAaaSubFolder("aaa/subfolder/foo");
+ const QString barFileAaaSubFolder("aaa/subfolder/bar");
+
+ fakeFolder.remoteModifier().insert(fooFileRootFolder);
+ fakeFolder.remoteModifier().insert(barFileRootFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileSubFolder);
+ fakeFolder.remoteModifier().insert(barFileSubFolder);
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa"));
+ fakeFolder.remoteModifier().mkdir(QStringLiteral("aaa/subfolder"));
+ fakeFolder.remoteModifier().insert(fooFileAaaSubFolder);
+ fakeFolder.remoteModifier().insert(barFileAaaSubFolder);
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileRootFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(fooFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.remoteModifier().setModTimeKeepEtag(barFileAaaSubFolder, QDateTime::fromSecsSinceEpoch(CURRENT_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileRootFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileRootFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileSubFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileSubFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(fooFileAaaSubFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+ fakeFolder.localModifier().setModTime(barFileAaaSubFolder + DVSUFFIX, QDateTime::fromSecsSinceEpoch(FUTURE_MTIME));
+
+ QVERIFY(fakeFolder.syncOnce());
+
+ QVERIFY(fakeFolder.syncOnce());
+ }
};
QTEST_GUILESS_MAIN(TestSyncVirtualFiles)