From: alex-z Date: Sun, 21 Apr 2024 21:48:01 +0000 (+0200) Subject: Unit tests for diverse conflicts in one folder. X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~7^2~43^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d09c15756843a855fceb16090eff79b034757fe7;p=nextcloud-desktop.git Unit tests for diverse conflicts in one folder. Signed-off-by: alex-z --- diff --git a/test/syncenginetestutils.cpp b/test/syncenginetestutils.cpp index a12261ce3..5e097a34b 100644 --- a/test/syncenginetestutils.cpp +++ b/test/syncenginetestutils.cpp @@ -258,6 +258,21 @@ FileInfo *FileInfo::find(PathComponents pathComponents, const bool invalidateEta return nullptr; } +FileInfo FileInfo::findRecursive(PathComponents pathComponents, const bool invalidateEtags) +{ + auto result = find({pathComponents.takeFirst()}); + if (!result) { + return *result; + } + for (const auto &pathComponent : pathComponents) { + if (!result) { + break; + } + result = result->find({pathComponent}); + } + return *result; +} + FileInfo *FileInfo::createDir(const QString &relativePath) { const PathComponents pathComponents { relativePath }; diff --git a/test/syncenginetestutils.h b/test/syncenginetestutils.h index c688eddce..49cdac710 100644 --- a/test/syncenginetestutils.h +++ b/test/syncenginetestutils.h @@ -147,6 +147,7 @@ public: void setE2EE(const QString &relativepath, const bool enabled) override; FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false); + FileInfo findRecursive(PathComponents pathComponents, const bool invalidateEtags = false); FileInfo *createDir(const QString &relativePath); diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 82a3c2390..59b41ffc4 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -1703,6 +1703,147 @@ private slots: QVERIFY(fakeFolder.syncOnce()); } + void testServer_caseClash_createDiverseConflictsInsideOneFolderAndSolveThem() + { + FakeFolder fakeFolder{FileInfo{}}; + + const QStringList conflictsFolderPathComponents = {"Documents", "DiverseConflicts"}; + + QString diverseConflictsFolderPath; + for (const auto &conflictsFolderPathComponent : conflictsFolderPathComponents) { + if (diverseConflictsFolderPath.isEmpty()) { + diverseConflictsFolderPath += conflictsFolderPathComponent; + } else { + diverseConflictsFolderPath += "/" + conflictsFolderPathComponent; + } + fakeFolder.remoteModifier().mkdir(diverseConflictsFolderPath); + } + + constexpr auto testLowerCaseFile = "testfile"; + constexpr auto testUpperCaseFile = "TESTFILE"; + + constexpr auto testLowerCaseFolder = "testfolder"; + constexpr auto testUpperCaseFolder = "TESTFOLDER"; + + constexpr auto testInvalidCharFolder = "Really?"; + + fakeFolder.remoteModifier().insert(diverseConflictsFolderPath + "/" + testLowerCaseFile); + fakeFolder.remoteModifier().insert(diverseConflictsFolderPath + "/" + testUpperCaseFile); + + fakeFolder.remoteModifier().mkdir(diverseConflictsFolderPath + "/" + testLowerCaseFolder); + fakeFolder.remoteModifier().mkdir(diverseConflictsFolderPath + "/" + testUpperCaseFolder); + + fakeFolder.remoteModifier().mkdir(diverseConflictsFolderPath + "/" + testInvalidCharFolder); + +#if defined Q_OS_LINUX + constexpr auto shouldHaveCaseClashConflict = false; +#else + constexpr auto shouldHaveCaseClashConflict = true; +#endif + + ItemCompletedSpy completeSpy(fakeFolder); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem); + QVERIFY(fakeFolder.syncOnce()); + + // verify the parent of a folder where caseclash and invalidchar conflicts were found, has corresponding flags set (conflict info must get propagated to the very top) + const auto diverseConflicttsFolderParent = completeSpy.findItem(conflictsFolderPathComponents.first()); + QVERIFY(diverseConflicttsFolderParent); + QVERIFY(diverseConflicttsFolderParent->_isAnyCaseClashChild); + QVERIFY(diverseConflicttsFolderParent->_isAnyInvalidCharChild); + completeSpy.clear(); + + auto diverseConflictsFolderInfo = fakeFolder.currentLocalState().findRecursive(diverseConflictsFolderPath); + QVERIFY(!diverseConflictsFolderInfo.name.isEmpty()); + + auto conflictsFile = findCaseClashConflicts(diverseConflictsFolderInfo); + QCOMPARE(conflictsFile.size(), shouldHaveCaseClashConflict ? 1 : 0); + const auto hasFileCaseClashConflict = expectConflict(diverseConflictsFolderInfo, testLowerCaseFile); + QCOMPARE(hasFileCaseClashConflict, shouldHaveCaseClashConflict ? true : false); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem); + QVERIFY(fakeFolder.syncOnce()); + + diverseConflictsFolderInfo = fakeFolder.currentLocalState().findRecursive(diverseConflictsFolderPath); + QVERIFY(!diverseConflictsFolderInfo.name.isEmpty()); + + conflictsFile = findCaseClashConflicts(diverseConflictsFolderInfo); + QCOMPARE(conflictsFile.size(), shouldHaveCaseClashConflict ? 1 : 0); + + if (shouldHaveCaseClashConflict) { + const auto conflictFileName = QString{conflictsFile.constFirst()}; + qDebug() << conflictFileName; + CaseClashConflictSolver conflictSolver(fakeFolder.localPath() + diverseConflictsFolderPath + "/" + testLowerCaseFile, + fakeFolder.localPath() + conflictFileName, + QStringLiteral("/"), + fakeFolder.localPath(), + fakeFolder.account(), + &fakeFolder.syncJournal()); + + QSignalSpy conflictSolverDone(&conflictSolver, &CaseClashConflictSolver::done); + + conflictSolver.solveConflict("testfile2"); + + QVERIFY(conflictSolverDone.wait()); + + QVERIFY(fakeFolder.syncOnce()); + + diverseConflictsFolderInfo = fakeFolder.currentLocalState().findRecursive(diverseConflictsFolderPath); + QVERIFY(!diverseConflictsFolderInfo.name.isEmpty()); + + conflictsFile = findCaseClashConflicts(diverseConflictsFolderInfo); + QCOMPARE(conflictsFile.size(), 0); + + fakeFolder.syncEngine().setLocalDiscoveryOptions(OCC::LocalDiscoveryStyle::DatabaseAndFilesystem); + QVERIFY(fakeFolder.syncOnce()); + + // After solving file conflict, verify that we did not lose conflict detection of caseclash and invalidchar for folders + for (auto it = completeSpy.begin(); it != completeSpy.end(); ++it) { + auto item = (*it).first().value(); + item = nullptr; + } + auto invalidFilenameConflictFolderItem = completeSpy.findItem(diverseConflictsFolderPath + "/" + testInvalidCharFolder); + QVERIFY(invalidFilenameConflictFolderItem); + QVERIFY(invalidFilenameConflictFolderItem->_status == SyncFileItem::FileNameInvalid); + + auto caseClashConflictFolderItemLower = completeSpy.findItem(diverseConflictsFolderPath + "/" + testLowerCaseFolder); + auto caseClashConflictFolderItemUpper = completeSpy.findItem(diverseConflictsFolderPath + "/" + testUpperCaseFolder); + completeSpy.clear(); + + // we always create UPPERCASE folder in current syncengine logic for now and then create a conflict for a lowercase folder, but this may change, so keep this check more future proof + const auto upperOrLowerCaseFolderCaseClashFound = + (caseClashConflictFolderItemLower && caseClashConflictFolderItemLower->_status == SyncFileItem::FileNameClash) + || (caseClashConflictFolderItemUpper && caseClashConflictFolderItemUpper->_status == SyncFileItem::FileNameClash); + + QVERIFY(upperOrLowerCaseFolderCaseClashFound); + + // solve case clash folders conflict + CaseClashConflictSolver conflictSolverCaseClashForFolder(fakeFolder.localPath() + diverseConflictsFolderPath + "/" + testLowerCaseFolder, + fakeFolder.localPath() + diverseConflictsFolderPath + "/" + testUpperCaseFolder, + QStringLiteral("/"), + fakeFolder.localPath(), + fakeFolder.account(), + &fakeFolder.syncJournal()); + + QSignalSpy conflictSolverCaseClashForFolderDone(&conflictSolverCaseClashForFolder, &CaseClashConflictSolver::done); + conflictSolverCaseClashForFolder.solveConflict("testfolder1"); + QVERIFY(conflictSolverCaseClashForFolderDone.wait()); + QVERIFY(fakeFolder.syncOnce()); + + // verify no case clash conflicts folder items are found + caseClashConflictFolderItemLower = completeSpy.findItem(diverseConflictsFolderPath + "/" + testLowerCaseFolder); + caseClashConflictFolderItemUpper = completeSpy.findItem(diverseConflictsFolderPath + "/" + testUpperCaseFolder); + QVERIFY((!caseClashConflictFolderItemLower || caseClashConflictFolderItemLower->_file.isEmpty()) + && (!caseClashConflictFolderItemUpper || caseClashConflictFolderItemUpper->_file.isEmpty())); + + // veriy invalid filename conflict folder item is still present + invalidFilenameConflictFolderItem = completeSpy.findItem(diverseConflictsFolderPath + "/" + testInvalidCharFolder); + completeSpy.clear(); + QVERIFY(invalidFilenameConflictFolderItem); + QVERIFY(invalidFilenameConflictFolderItem->_status == SyncFileItem::FileNameInvalid); + } + } + void testExistingFolderBecameBig() { constexpr auto testFolder = "folder";