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

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorErik Verbruggen <erik@verbruggen.consulting>2021-12-08 17:23:32 +0300
committerErik Verbruggen <Erik.Verbruggen@Me.com>2021-12-09 15:28:23 +0300
commit108fcadce3a87dbf26abdb0126ed21298d2bdd69 (patch)
treea7391d1a4e4e49b6481b402b58a0827bd260c64a /test
parentb7e25e3f26785c3e3667471539234e5d4bafef9f (diff)
Support dehydrated files in autotests
When creating the local state, check if files are hydrated or not. If not hydrated, don't read the file: the OS will trigger a download. This is bad: first the read will fail, because the test is running on the main thread, the same place where work from callbacks from the OS get handled. This will result in a time-out for the OS, and it will return 0 bytes read. So the size for the file in our local state is set to zero bytes, which makes the comparisson with the remote state fail, which in turn makes the comparisson fail. Worse: the callbacks from the system do come in, and are emitted as a _QueuedConnection_. So when another call to `syncOnce` is made, the queued downloads will be done, and the file will be re-hydrated, thus changing the state of the files on disk.
Diffstat (limited to 'test')
-rw-r--r--test/testchunkingng.cpp37
-rw-r--r--test/testpermissions.cpp12
-rw-r--r--test/testsyncconflict.cpp14
-rw-r--r--test/testsyncmove.cpp62
-rw-r--r--test/testsyncvirtualfiles.cpp6
-rw-r--r--test/testutils/syncenginetestutils.cpp95
-rw-r--r--test/testutils/syncenginetestutils.h39
7 files changed, 193 insertions, 72 deletions
diff --git a/test/testchunkingng.cpp b/test/testchunkingng.cpp
index e7add5b4f..0cab9a090 100644
--- a/test/testchunkingng.cpp
+++ b/test/testchunkingng.cpp
@@ -36,8 +36,7 @@ static void partialUpload(FakeFolder &fakeFolder, const QString &name, qint64 si
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // the transfer was done with chunking
auto upStateChildren = fakeFolder.uploadState().children.first().children;
- QCOMPARE(sizeWhenAbort, std::accumulate(upStateChildren.cbegin(), upStateChildren.cend(), 0,
- [](int s, const FileInfo &i) { return s + i.size; }));
+ QCOMPARE(sizeWhenAbort, std::accumulate(upStateChildren.cbegin(), upStateChildren.cend(), 0, [](int s, const FileInfo &i) { return s + i.contentSize; }));
}
// Reduce max chunk size a bit so we get more chunks
@@ -66,7 +65,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // the transfer was done with chunking
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// Check that another upload of the same file also work.
fakeFolder.localModifier().appendByte("A/a0");
@@ -86,7 +85,7 @@ private slots:
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
const auto &chunkMap = fakeFolder.uploadState().children.first().children;
- qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.size; });
+ qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.contentSize; });
QVERIFY(uploadedSize > 2 * 1000 * 1000); // at least 2 MB
// Add a fake chunk to make sure it gets deleted
@@ -105,7 +104,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// The same chunk id was re-used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QCOMPARE(fakeFolder.uploadState().children.first().name, chunkingId);
@@ -121,7 +120,7 @@ private slots:
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
const auto &chunkMap = fakeFolder.uploadState().children.first().children;
- qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.size; });
+ qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.contentSize; });
QVERIFY(uploadedSize > 2 * 1000 * 1000); // at least 50 MB
QVERIFY(chunkMap.size() >= 3); // at least three chunks
@@ -139,7 +138,7 @@ private slots:
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *) -> QNetworkReply * {
if (op == QNetworkAccessManager::PutOperation) {
// Test that we properly resuming, not resending the first chunk
- Q_ASSERT(request.rawHeader("OC-Chunk-Offset").toLongLong() >= firstChunk.size);
+ Q_ASSERT(request.rawHeader("OC-Chunk-Offset").toLongLong() >= firstChunk.contentSize);
} else if (op == QNetworkAccessManager::DeleteOperation) {
deletedPaths.append(request.url().path());
}
@@ -160,7 +159,7 @@ private slots:
}
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// The same chunk id was re-used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QCOMPARE(fakeFolder.uploadState().children.first().name, chunkingId);
@@ -177,7 +176,7 @@ private slots:
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
const auto &chunkMap = fakeFolder.uploadState().children.first().children;
- qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.size; });
+ qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.contentSize; });
QVERIFY(uploadedSize > 5 * 1000 * 1000); // at least 5 MB
// Add a chunk that makes the file completely uploaded
@@ -204,7 +203,7 @@ private slots:
QVERIFY(!sawDelete);
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// The same chunk id was re-used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QCOMPARE(fakeFolder.uploadState().children.first().name, chunkingId);
@@ -222,7 +221,7 @@ private slots:
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
const auto &chunkMap = fakeFolder.uploadState().children.first().children;
- qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.size; });
+ qint64 uploadedSize = std::accumulate(chunkMap.begin(), chunkMap.end(), 0LL, [](qint64 s, const FileInfo &f) { return s + f.contentSize; });
QVERIFY(uploadedSize > 5 * 1000 * 1000); // at least 5 MB
// Add a chunk that makes the file more than completely uploaded
@@ -232,7 +231,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
}
@@ -384,7 +383,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size + 1);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size + 1);
// A different chunk id was used, and the previous one is removed
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QVERIFY(fakeFolder.uploadState().children.first().name != chunkingId);
@@ -443,7 +442,7 @@ private slots:
auto localState = fakeFolder.currentLocalState();
// A0 is the one from the server
- QCOMPARE(localState.find("A/a0")->size, size);
+ QCOMPARE(localState.find("A/a0")->contentSize, size);
QCOMPARE(localState.find("A/a0")->contentChar, 'C');
// There is a conflict file with our version
@@ -453,7 +452,7 @@ private slots:
});
QVERIFY(it != stateAChildren.cend());
QCOMPARE(it->contentChar, 'B');
- QCOMPARE(it->size, size+1);
+ QCOMPARE(it->contentSize, size + 1);
// Remove the conflict file so the comparison works!
fakeFolder.localModifier().remove("A/" + it->name);
@@ -494,7 +493,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size+1);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size + 1);
// A different chunk id was used, and the previous one is removed
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
@@ -517,7 +516,7 @@ private slots:
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// A different chunk id was used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
@@ -618,7 +617,7 @@ private slots:
// Now resume
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size);
// The same chunk id was re-used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
@@ -629,7 +628,7 @@ private slots:
fakeFolder.localModifier().appendByte("A/a0");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
- QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size + 1);
+ QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->contentSize, size + 1);
}
diff --git a/test/testpermissions.cpp b/test/testpermissions.cpp
index b1dc39378..e0cafd0a3 100644
--- a/test/testpermissions.cpp
+++ b/test/testpermissions.cpp
@@ -160,22 +160,22 @@ private slots:
//3.
// File should be recovered
- QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
- QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")->size, cannotBeModifiedSize);
+ QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data")->contentSize, cannotBeModifiedSize);
+ QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data")->contentSize, cannotBeModifiedSize);
// and conflict created
auto c1 = findConflict(currentLocalState, "normalDirectory_PERM_CKDNV_/cannotBeModified_PERM_DVN_.data");
QVERIFY(c1);
- QCOMPARE(c1->size, cannotBeModifiedSize + 1);
+ QCOMPARE(c1->contentSize, cannotBeModifiedSize + 1);
auto c2 = findConflict(currentLocalState, "readonlyDirectory_PERM_M_/cannotBeModified_PERM_DVN_.data");
QVERIFY(c2);
- QCOMPARE(c2->size, cannotBeModifiedSize + 1);
+ QCOMPARE(c2->contentSize, cannotBeModifiedSize + 1);
// remove the conflicts for the next state comparison
fakeFolder.localModifier().remove(c1->path());
fakeFolder.localModifier().remove(c2->path());
//4. File should be updated, that's tested by assertLocalAndRemoteDir
- QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
- QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data")->size, canBeModifiedSize + 1);
+ QCOMPARE(currentLocalState.find("normalDirectory_PERM_CKDNV_/canBeModified_PERM_W_.data")->contentSize, canBeModifiedSize + 1);
+ QCOMPARE(currentLocalState.find("readonlyDirectory_PERM_M_/canBeModified_PERM_W_.data")->contentSize, canBeModifiedSize + 1);
//5.
// the file should be in the server and local
diff --git a/test/testsyncconflict.cpp b/test/testsyncconflict.cpp
index dde1eb751..450569e92 100644
--- a/test/testsyncconflict.cpp
+++ b/test/testsyncconflict.cpp
@@ -136,8 +136,8 @@ private slots:
QCOMPARE(remote.find(conflictMap[a1FileId])->contentChar, 'L');
QCOMPARE(remote.find("A/a1")->contentChar, 'R');
- QCOMPARE(remote.find(conflictMap[a2FileId])->size, 5);
- QCOMPARE(remote.find("A/a2")->size, 6);
+ QCOMPARE(remote.find(conflictMap[a2FileId])->contentSize, 5);
+ QCOMPARE(remote.find("A/a2")->contentSize, 6);
}
void testSeparateUpload()
@@ -206,8 +206,8 @@ private slots:
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(conflictMap.size(), 1);
QVERIFY(conflictMap.contains(a1ConflictFileId));
- QCOMPARE(fakeFolder.currentRemoteState().find(conflictName)->size, 66);
- QCOMPARE(fakeFolder.currentRemoteState().find(conflictMap[a1ConflictFileId])->size, 65);
+ QCOMPARE(fakeFolder.currentRemoteState().find(conflictName)->contentSize, 66);
+ QCOMPARE(fakeFolder.currentRemoteState().find(conflictMap[a1ConflictFileId])->contentSize, 65);
conflictMap.clear();
}
@@ -440,7 +440,7 @@ private slots:
// 1)
QVERIFY(itemConflict(completeSpy, "Z"));
- QCOMPARE(fakeFolder.currentLocalState().find("Z")->size, 63);
+ QCOMPARE(fakeFolder.currentLocalState().find("Z")->contentSize, 63);
QVERIFY(conflicts[2].contains("Z"));
QCOMPARE(conflicts[2].toUtf8(), conflictRecords[2]);
QVERIFY(QFileInfo(fakeFolder.localPath() + conflicts[2]).isDir());
@@ -448,7 +448,7 @@ private slots:
// 2)
QVERIFY(itemConflict(completeSpy, "A/a1"));
- QCOMPARE(fakeFolder.currentLocalState().find("A/a1")->size, 5);
+ QCOMPARE(fakeFolder.currentLocalState().find("A/a1")->contentSize, 5);
QVERIFY(conflicts[0].contains("A/a1"));
QCOMPARE(conflicts[0].toUtf8(), conflictRecords[0]);
QVERIFY(QFileInfo(fakeFolder.localPath() + conflicts[0]).isDir());
@@ -456,7 +456,7 @@ private slots:
// 3)
QVERIFY(itemConflict(completeSpy, "B"));
- QCOMPARE(fakeFolder.currentLocalState().find("B")->size, 31);
+ QCOMPARE(fakeFolder.currentLocalState().find("B")->contentSize, 31);
QVERIFY(conflicts[1].contains("B"));
QCOMPARE(conflicts[1].toUtf8(), conflictRecords[1]);
QVERIFY(QFileInfo(fakeFolder.localPath() + conflicts[1]).isDir());
diff --git a/test/testsyncmove.cpp b/test/testsyncmove.cpp
index 047e5149b..c82bba3d2 100644
--- a/test/testsyncmove.cpp
+++ b/test/testsyncmove.cpp
@@ -841,21 +841,74 @@ private slots:
}
+ void testRenameParallelism_data()
+ {
+ QTest::addColumn<Vfs::Mode>("vfsMode");
+ QTest::addColumn<bool>("filesAreDehydrated");
+
+ QTest::newRow("Vfs::Off") << Vfs::Off << true;
+
+ if (isVfsPluginAvailable(Vfs::WindowsCfApi)) {
+ QTest::newRow("Vfs::WindowsCfApi dehydrated") << Vfs::WindowsCfApi << true;
+
+ // TODO: then hydrated version will fail due to an issue in the winvfs plugin, so leave it disabled for now.
+ // QTest::newRow("Vfs::WindowsCfApi hydrated") << Vfs::WindowsCfApi << false;
+ } else if (Utility::isWindows()) {
+ QWARN("Skipping Vfs::WindowsCfApi");
+ }
+ }
+
// Test that deletes don't run before renames
void testRenameParallelism()
{
- FakeFolder fakeFolder{ FileInfo{} };
+ QFETCH(Vfs::Mode, vfsMode);
+ QFETCH(bool, filesAreDehydrated);
+
+ FakeFolder fakeFolder({ FileInfo {} }, vfsMode);
+
+ FileInfo::ComparissonOption cmpOpt = FileInfo::ContentIsKing;
+ if (vfsMode != Vfs::Off) {
+ auto vfs = QSharedPointer<Vfs>(createVfsFromPlugin(vfsMode).release());
+ QVERIFY(vfs);
+ fakeFolder.switchToVfs(vfs);
+ fakeFolder.syncJournal().internalPinStates().setForPath("", filesAreDehydrated ? PinState::OnlineOnly : PinState::AlwaysLocal);
+
+ // make files virtual
+ fakeFolder.syncOnce();
+
+ cmpOpt = FileInfo::IgnoreContentOfDehydratedFiles;
+ }
+
fakeFolder.remoteModifier().mkdir("A");
fakeFolder.remoteModifier().insert("A/file");
QVERIFY(fakeFolder.syncOnce());
- QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ {
+ auto localState = fakeFolder.currentLocalState();
+ FileInfo *file = localState.find({ "A/file" });
+ QVERIFY(file != nullptr); // check if the file exists
+ if (vfsMode != Vfs::Off) {
+ QCOMPARE(file->isDehydratedPlaceholder, filesAreDehydrated);
+ }
+ QVERIFY(localState.equals(fakeFolder.currentRemoteState(), cmpOpt));
+ }
fakeFolder.localModifier().mkdir("B");
fakeFolder.localModifier().rename("A/file", "B/file");
fakeFolder.localModifier().remove("A");
-
QVERIFY(fakeFolder.syncOnce());
- QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ {
+ auto localState = fakeFolder.currentLocalState();
+ QVERIFY(localState.find("A/file") == nullptr); // check if the file is gone
+ QVERIFY(localState.find("A") == nullptr); // check if the directory is gone
+ FileInfo *file = localState.find({ "B/file" });
+ QVERIFY(file != nullptr); // check if the file exists
+ if (vfsMode != Vfs::Off) {
+ QCOMPARE(file->isDehydratedPlaceholder, filesAreDehydrated); // check that no-one messed with the placeholder state
+ }
+ QVERIFY(localState.equals(fakeFolder.currentRemoteState(), cmpOpt));
+ }
}
void testMovedWithError_data()
@@ -871,7 +924,6 @@ private slots:
} else {
QWARN("Skipping Vfs::WindowsCfApi");
}
-
#endif
}
diff --git a/test/testsyncvirtualfiles.cpp b/test/testsyncvirtualfiles.cpp
index 627af1a2a..e52c46475 100644
--- a/test/testsyncvirtualfiles.cpp
+++ b/test/testsyncvirtualfiles.cpp
@@ -601,7 +601,7 @@ private slots:
QVERIFY(!fakeFolder.currentLocalState().find("A/a1"));
QVERIFY(fakeFolder.currentLocalState().find("A/a1" DVSUFFIX));
- QVERIFY(fakeFolder.currentLocalState().find("A/a1" DVSUFFIX)->size <= 1);
+ QVERIFY(fakeFolder.currentLocalState().find("A/a1" DVSUFFIX)->contentSize <= 1);
QVERIFY(fakeFolder.currentRemoteState().find("A/a1"));
QCOMPARE(itemInstruction(completeSpy, "A/a1" DVSUFFIX), CSYNC_INSTRUCTION_SYNC);
QCOMPARE(dbRecord(fakeFolder, "A/a1" DVSUFFIX)._type, ItemTypeVirtualFile);
@@ -840,10 +840,10 @@ private slots:
QCOMPARE(itemInstruction(completeSpy, "B/b2"), CSYNC_INSTRUCTION_REMOVE);
QCOMPARE(itemInstruction(completeSpy, "B/b3" DVSUFFIX), CSYNC_INSTRUCTION_NEW);
- QCOMPARE(fakeFolder.currentRemoteState().find("C/c1")->size, 25);
+ QCOMPARE(fakeFolder.currentRemoteState().find("C/c1")->contentSize, 25);
QCOMPARE(itemInstruction(completeSpy, "C/c1"), CSYNC_INSTRUCTION_SYNC);
- QCOMPARE(fakeFolder.currentRemoteState().find("C/c2")->size, 26);
+ QCOMPARE(fakeFolder.currentRemoteState().find("C/c2")->contentSize, 26);
QCOMPARE(itemInstruction(completeSpy, "C/c2"), CSYNC_INSTRUCTION_CONFLICT);
cleanup();
diff --git a/test/testutils/syncenginetestutils.cpp b/test/testutils/syncenginetestutils.cpp
index be3d7f984..7edcb6e19 100644
--- a/test/testutils/syncenginetestutils.cpp
+++ b/test/testutils/syncenginetestutils.cpp
@@ -132,11 +132,12 @@ FileInfo::FileInfo(const QString &name, const std::initializer_list<FileInfo> &c
addChild(source);
}
-void FileInfo::addChild(const FileInfo &info)
+FileInfo &FileInfo::addChild(const FileInfo &info)
{
- auto &dest = this->children[info.name] = info;
+ FileInfo &dest = this->children[info.name] = info;
dest.parentPath = path();
dest.fixupParentPathRecursively();
+ return dest;
}
void FileInfo::remove(const QString &relativePath)
@@ -165,7 +166,7 @@ void FileInfo::appendByte(const QString &relativePath, char contentChar)
Q_UNUSED(contentChar);
FileInfo *file = findInvalidatingEtags(relativePath);
Q_ASSERT(file);
- file->size += 1;
+ file->contentSize += 1;
}
void FileInfo::modifyByte(const QString &relativePath, quint64 offset, char contentChar)
@@ -254,11 +255,41 @@ bool FileInfo::operator==(const FileInfo &other) const
// Consider files to be equal between local<->remote as a user would.
return name == other.name
&& isDir == other.isDir
- && size == other.size
+ && contentSize == other.contentSize
&& contentChar == other.contentChar
&& children == other.children;
}
+bool FileInfo::equals(const FileInfo &other, ComparissonOption opt) const
+{
+ switch (opt) {
+ case ContentIsKing:
+ return *this == other;
+ case IgnoreContentOfDehydratedFiles:
+ if (!isDehydratedPlaceholder && !other.isDehydratedPlaceholder) {
+ if (contentSize != other.contentSize || contentChar != other.contentChar) {
+ return false;
+ }
+ }
+
+ if (name == other.name && isDir == other.isDir && fileSize == other.fileSize && lastModified == other.lastModified) {
+ if (children.size() == other.children.size()) {
+ for (auto it = children.constBegin(), eit = children.constEnd(),
+ oit = other.children.constBegin(), oeit = other.children.constEnd();
+ it != eit && oit != oeit; ++it, ++oit) {
+ if (!it->equals(*oit, opt)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ Q_UNREACHABLE();
+}
+
QString FileInfo::path() const
{
return (parentPath.isEmpty() ? QString() : (parentPath + QLatin1Char('/'))) + name;
@@ -332,7 +363,7 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
auto gmtDate = fileInfo.lastModified.toUTC();
auto stringDate = QLocale::c().toString(gmtDate, QStringLiteral("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
xml.writeTextElement(davUri, QStringLiteral("getlastmodified"), stringDate);
- xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.size));
+ xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.contentSize));
xml.writeTextElement(davUri, QStringLiteral("getetag"), QStringLiteral("\"%1\"").arg(QString::fromLatin1(fileInfo.etag)));
xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("SRDNVCKW")
: QStringLiteral("RDNVCKW"));
@@ -409,8 +440,9 @@ FileInfo *FakePutReply::perform(FileInfo &remoteRootFileInfo, const QNetworkRequ
Q_ASSERT(!fileName.isEmpty());
FileInfo *fileInfo = remoteRootFileInfo.find(fileName);
if (fileInfo) {
- fileInfo->size = putPayload.size();
+ fileInfo->contentSize = putPayload.size();
fileInfo->contentChar = putPayload.at(0);
+ fileInfo->fileSize = fileInfo->contentSize; // it's hydrated on the server, so these are the same
} else {
// Assume that the file is filled with the same character
fileInfo = remoteRootFileInfo.create(fileName, putPayload.size(), putPayload.at(0));
@@ -422,7 +454,7 @@ FileInfo *FakePutReply::perform(FileInfo &remoteRootFileInfo, const QNetworkRequ
void FakePutReply::respond()
{
- emit uploadProgress(fileInfo->size, fileInfo->size);
+ emit uploadProgress(fileInfo->contentSize, fileInfo->contentSize);
setRawHeader("OC-ETag", fileInfo->etag);
setRawHeader("ETag", fileInfo->etag);
setRawHeader("OC-FileID", fileInfo->fileId);
@@ -536,7 +568,7 @@ void FakeGetReply::respond()
return;
}
payload = fileInfo->contentChar;
- size = fileInfo->size;
+ size = fileInfo->contentSize;
setHeader(QNetworkRequest::ContentLengthHeader, size);
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
setRawHeader("OC-ETag", fileInfo->etag);
@@ -677,12 +709,12 @@ FileInfo *FakeChunkMoveReply::perform(FileInfo &uploadsFileInfo, FileInfo &remot
if (chunkNameLongLong != prev)
break;
Q_ASSERT(!x.isDir);
- Q_ASSERT(x.size > 0); // There should not be empty chunks
- size += x.size;
+ Q_ASSERT(x.contentSize > 0); // There should not be empty chunks
+ size += x.contentSize;
Q_ASSERT(!payload || payload == x.contentChar);
payload = x.contentChar;
++count;
- prev = chunkNameLongLong + x.size;
+ prev = chunkNameLongLong + x.contentSize;
}
Q_ASSERT(sourceFolderChildren.count() == count); // There should not be holes or extra files
@@ -699,8 +731,9 @@ FileInfo *FakeChunkMoveReply::perform(FileInfo &uploadsFileInfo, FileInfo &remot
if (request.rawHeader("If") != start + " ([\"" + fileInfo->etag + "\"])") {
return nullptr;
}
- fileInfo->size = size;
+ fileInfo->contentSize = size;
fileInfo->contentChar = payload;
+ fileInfo->fileSize = fileInfo->contentSize; // it's hydrated on the server, so these are the same
} else {
Q_ASSERT(!request.hasRawHeader("If"));
// Assume that the file is filled with the same character
@@ -881,8 +914,9 @@ QNetworkReply *FakeQNAM::createRequest(QNetworkAccessManager::Operation op, cons
return reply;
}
-FakeFolder::FakeFolder(const FileInfo &fileTemplate)
+FakeFolder::FakeFolder(const FileInfo &fileTemplate, OCC::Vfs::Mode vfsMode)
: _localModifier(_tempDir.path())
+ , _vfsMode(vfsMode)
{
// Needs to be done once
OCC::SyncEngine::minimumFileAgeForUpload = std::chrono::milliseconds(0);
@@ -1000,6 +1034,11 @@ void FakeFolder::execUntilItemCompleted(const QString &relativePath)
QVERIFY(false);
}
+bool FakeFolder::isDehydratedPlaceholder(const QString &filePath)
+{
+ return _syncEngine->syncOptions()._vfs->isDehydratedPlaceholder(filePath);
+}
+
void FakeFolder::toDisk(QDir &dir, const FileInfo &templateFi)
{
for (const auto &child : templateFi.children) {
@@ -1011,7 +1050,7 @@ void FakeFolder::toDisk(QDir &dir, const FileInfo &templateFi)
} else {
QFile file { dir.filePath(child.name) };
file.open(QFile::WriteOnly);
- file.write(QByteArray {}.fill(child.contentChar, child.size));
+ file.write(QByteArray {}.fill(child.contentChar, child.contentSize));
file.close();
OCC::FileSystem::setModTime(file.fileName(), OCC::Utility::qDateTimeToTime_t(child.lastModified));
}
@@ -1028,15 +1067,25 @@ void FakeFolder::fromDisk(QDir &dir, FileInfo &templateFi)
FileInfo &subFi = templateFi.children[diskChild.fileName()] = FileInfo { diskChild.fileName() };
fromDisk(subDir, subFi);
} else {
- QFile f { diskChild.filePath() };
- f.open(QFile::ReadOnly);
- auto content = f.read(1);
- if (content.size() == 0) {
- qWarning() << "Empty file at:" << diskChild.filePath();
- continue;
+ FileInfo fi(diskChild.fileName());
+ fi.isDir = false;
+ fi.fileSize = diskChild.size();
+ fi.isDehydratedPlaceholder = isDehydratedPlaceholder(diskChild.absoluteFilePath());
+ if (fi.isDehydratedPlaceholder) {
+ fi.contentChar = '\0';
+ } else {
+ QFile f { diskChild.filePath() };
+ f.open(QFile::ReadOnly);
+ auto content = f.read(1);
+ if (content.size() == 0) {
+ qWarning() << "Empty file at:" << diskChild.filePath();
+ continue;
+ }
+ fi.contentChar = content.at(0);
+ fi.contentSize = fi.fileSize;
}
- char contentChar = content.at(0);
- templateFi.children.insert(diskChild.fileName(), FileInfo { diskChild.fileName(), diskChild.size(), contentChar });
+
+ templateFi.children.insert(fi.name, fi);
}
}
}
@@ -1065,7 +1114,7 @@ FileInfo FakeFolder::dbState() const
auto &item = parentDir.children[name];
item.name = name;
item.parentPath = parentDir.path();
- item.size = record._fileSize;
+ item.contentSize = record._fileSize;
item.isDir = record._type == ItemTypeDirectory;
item.permissions = record._remotePerm;
item.etag = record._etag;
diff --git a/test/testutils/syncenginetestutils.h b/test/testutils/syncenginetestutils.h
index 9f8f31a2f..22b0fa296 100644
--- a/test/testutils/syncenginetestutils.h
+++ b/test/testutils/syncenginetestutils.h
@@ -109,6 +109,7 @@ public:
void setModTime(const QString &relativePath, const QDateTime &modTime) override;
};
+/// FIXME: we should make it explicit in the construtor if we're talking about a hydrated or a dehydrated file!
class FileInfo : public FileModifier
{
public:
@@ -122,19 +123,21 @@ public:
FileInfo(const QString &name, qint64 size)
: name { name }
, isDir { false }
- , size { size }
+ , fileSize(size)
+ , contentSize { size }
{
}
FileInfo(const QString &name, qint64 size, char contentChar)
: name { name }
, isDir { false }
- , size { size }
+ , fileSize(size)
+ , contentSize { size }
, contentChar { contentChar }
{
}
FileInfo(const QString &name, const std::initializer_list<FileInfo> &children);
- void addChild(const FileInfo &info);
+ FileInfo &addChild(const FileInfo &info);
void remove(const QString &relativePath) override;
@@ -152,6 +155,7 @@ public:
void setModTime(const QString &relativePath, const QDateTime &modTime) override;
+ /// Return a pointer to the FileInfo, or a nullptr if it doesn't exist
FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false);
FileInfo *createDir(const QString &relativePath);
@@ -170,6 +174,13 @@ public:
return !operator==(other);
}
+ enum ComparissonOption {
+ IgnoreContentOfDehydratedFiles,
+ ContentIsKing,
+ };
+
+ bool equals(const FileInfo &other, ComparissonOption opt) const;
+
QString path() const;
QString absolutePath() const;
@@ -184,8 +195,10 @@ public:
QByteArray fileId = generateFileId();
QByteArray checksums;
QByteArray extraDavProperties;
- qint64 size = 0;
+ qint64 fileSize = 0;
+ qint64 contentSize = 0;
char contentChar = 'W';
+ bool isDehydratedPlaceholder = false;
// Sorted by name to be able to compare trees
QMap<QString, FileInfo> children;
@@ -195,7 +208,12 @@ public:
friend inline QDebug operator<<(QDebug dbg, const FileInfo &fi)
{
- return dbg << "{ " << fi.path() << ": " << fi.children;
+ return dbg << "{ " << fi.path() << ": "
+ << ", fileSize:" << fi.fileSize
+ << ", contentSize:" << fi.contentSize
+ << ", contentChar:" << fi.contentChar
+ << ", isDehydratedPlaceholder:" << fi.isDehydratedPlaceholder
+ << ", children:" << fi.children;
}
};
@@ -471,9 +489,10 @@ class FakeFolder
OCC::AccountPtr _account;
std::unique_ptr<OCC::SyncJournalDb> _journalDb;
std::unique_ptr<OCC::SyncEngine> _syncEngine;
+ OCC::Vfs::Mode _vfsMode;
public:
- FakeFolder(const FileInfo &fileTemplate);
+ FakeFolder(const FileInfo &fileTemplate, OCC::Vfs::Mode vfsMode = OCC::Vfs::Off);
void switchToVfs(QSharedPointer<OCC::Vfs> vfs);
@@ -523,10 +542,12 @@ public:
return execUntilFinished();
}
+ bool isDehydratedPlaceholder(const QString &filePath);
+
private:
static void toDisk(QDir &dir, const FileInfo &templateFi);
- static void fromDisk(QDir &dir, FileInfo &templateFi);
+ void fromDisk(QDir &dir, FileInfo &templateFi);
};
@@ -571,7 +592,7 @@ inline void addFiles(QStringList &dest, const FileInfo &fi)
for (const auto &fi : fi.children)
addFiles(dest, fi);
} else {
- dest += QStringLiteral("%1 - %2 %3-bytes").arg(fi.path()).arg(fi.size).arg(fi.contentChar);
+ dest += QStringLiteral("%1 - %2 %3-bytes").arg(fi.path()).arg(fi.contentSize).arg(fi.contentChar);
}
}
@@ -597,7 +618,7 @@ inline void addFilesDbData(QStringList &dest, const FileInfo &fi)
for (const auto &fi : fi.children)
addFilesDbData(dest, fi);
} else {
- dest += QStringLiteral("%1 - %2 %3 %4 %5").arg(fi.name, fi.isDir ? QStringLiteral("dir") : QStringLiteral("file"), QString::number(fi.size), QString::number(fi.lastModified.toSecsSinceEpoch()), QString::fromUtf8(fi.fileId));
+ dest += QStringLiteral("%1 - %2 %3 %4 %5").arg(fi.name, fi.isDir ? QStringLiteral("dir") : QStringLiteral("file"), QString::number(fi.contentSize), QString::number(fi.lastModified.toSecsSinceEpoch()), QString::fromUtf8(fi.fileId));
}
}