/* * 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 #include "syncenginetestutils.h" #include #include using namespace OCC; class TestLocalDiscovery : public QObject { Q_OBJECT private slots: // Check correct behavior when local discovery is partially drawn from the db void testLocalDiscoveryStyle() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; LocalDiscoveryTracker tracker; connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted); connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished); // More subdirectories are useful for testing fakeFolder.localModifier().mkdir("A/X"); fakeFolder.localModifier().mkdir("A/Y"); fakeFolder.localModifier().insert("A/X/x1"); fakeFolder.localModifier().insert("A/Y/y1"); tracker.addTouchedPath("A/X"); tracker.startSyncFullDiscovery(); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); QVERIFY(tracker.localDiscoveryPaths().empty()); // Test begins fakeFolder.localModifier().insert("A/a3"); fakeFolder.localModifier().insert("A/X/x2"); fakeFolder.localModifier().insert("A/Y/y2"); fakeFolder.localModifier().insert("B/b3"); fakeFolder.remoteModifier().insert("C/c3"); fakeFolder.remoteModifier().appendByte("C/c1"); tracker.addTouchedPath("A/X"); fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); tracker.startSyncPartialDiscovery(); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find("A/a3")); QVERIFY(fakeFolder.currentRemoteState().find("A/X/x2")); QVERIFY(!fakeFolder.currentRemoteState().find("A/Y/y2")); QVERIFY(!fakeFolder.currentRemoteState().find("B/b3")); QVERIFY(fakeFolder.currentLocalState().find("C/c3")); QCOMPARE(fakeFolder.syncEngine().lastLocalDiscoveryStyle(), LocalDiscoveryStyle::DatabaseAndFilesystem); QVERIFY(tracker.localDiscoveryPaths().empty()); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); QCOMPARE(fakeFolder.syncEngine().lastLocalDiscoveryStyle(), LocalDiscoveryStyle::FilesystemOnly); QVERIFY(tracker.localDiscoveryPaths().empty()); } void testLocalDiscoveryDecision() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; auto &engine = fakeFolder.syncEngine(); QVERIFY(engine.shouldDiscoverLocally("")); QVERIFY(engine.shouldDiscoverLocally("A")); QVERIFY(engine.shouldDiscoverLocally("A/X")); fakeFolder.syncEngine().setLocalDiscoveryOptions( LocalDiscoveryStyle::DatabaseAndFilesystem, { "A/X", "A/X space", "A/X/beta", "foo bar space/touch", "foo/", "zzz", "zzzz" }); QVERIFY(engine.shouldDiscoverLocally("")); QVERIFY(engine.shouldDiscoverLocally("A")); QVERIFY(engine.shouldDiscoverLocally("A/X")); QVERIFY(!engine.shouldDiscoverLocally("B")); QVERIFY(!engine.shouldDiscoverLocally("A B")); QVERIFY(!engine.shouldDiscoverLocally("B/X")); QVERIFY(engine.shouldDiscoverLocally("foo bar space")); QVERIFY(engine.shouldDiscoverLocally("foo")); QVERIFY(!engine.shouldDiscoverLocally("foo bar")); QVERIFY(!engine.shouldDiscoverLocally("foo bar/touch")); // These are within "A/X" so they should be discovered QVERIFY(engine.shouldDiscoverLocally("A/X/alpha")); QVERIFY(engine.shouldDiscoverLocally("A/X beta")); QVERIFY(engine.shouldDiscoverLocally("A/X/Y")); QVERIFY(engine.shouldDiscoverLocally("A/X space")); QVERIFY(engine.shouldDiscoverLocally("A/X space/alpha")); QVERIFY(!engine.shouldDiscoverLocally("A/Xylo/foo")); QVERIFY(engine.shouldDiscoverLocally("zzzz/hello")); QVERIFY(!engine.shouldDiscoverLocally("zzza/hello")); QEXPECT_FAIL("", "There is a possibility of false positives if the set contains a path " "which is a prefix, and that prefix is followed by a character less than '/'", Continue); QVERIFY(!engine.shouldDiscoverLocally("A/X o")); fakeFolder.syncEngine().setLocalDiscoveryOptions( LocalDiscoveryStyle::DatabaseAndFilesystem, {}); QVERIFY(!engine.shouldDiscoverLocally("")); } // Check whether item success and item failure adjusts the // tracker correctly. void testTrackerItemCompletion() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; LocalDiscoveryTracker tracker; connect(&fakeFolder.syncEngine(), &SyncEngine::itemCompleted, &tracker, &LocalDiscoveryTracker::slotItemCompleted); connect(&fakeFolder.syncEngine(), &SyncEngine::finished, &tracker, &LocalDiscoveryTracker::slotSyncFinished); auto trackerContains = [&](const char *path) { return tracker.localDiscoveryPaths().find(path) != tracker.localDiscoveryPaths().end(); }; tracker.addTouchedPath("A/spurious"); fakeFolder.localModifier().insert("A/a3"); tracker.addTouchedPath("A/a3"); fakeFolder.localModifier().insert("A/a4"); fakeFolder.serverErrorPaths().append("A/a4"); // We're not adding a4 as touched, it's in the same folder as a3 and will be seen. // And due to the error it should be added to the explicit list while a3 gets removed. fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); tracker.startSyncPartialDiscovery(); QVERIFY(!fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find("A/a3")); QVERIFY(!fakeFolder.currentRemoteState().find("A/a4")); QVERIFY(!trackerContains("A/a3")); QVERIFY(trackerContains("A/a4")); QVERIFY(trackerContains("A/spurious")); // not removed since overall sync not successful fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::FilesystemOnly); tracker.startSyncFullDiscovery(); QVERIFY(!fakeFolder.syncOnce()); QVERIFY(!fakeFolder.currentRemoteState().find("A/a4")); QVERIFY(trackerContains("A/a4")); // had an error, still here QVERIFY(!trackerContains("A/spurious")); // removed due to full discovery fakeFolder.serverErrorPaths().clear(); fakeFolder.syncJournal().wipeErrorBlacklist(); tracker.addTouchedPath("A/newspurious"); // will be removed due to successful sync fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, tracker.localDiscoveryPaths()); tracker.startSyncPartialDiscovery(); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find("A/a4")); QVERIFY(tracker.localDiscoveryPaths().empty()); } void testDirectoryAndSubDirectory() { FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; fakeFolder.localModifier().mkdir("A/newDir"); fakeFolder.localModifier().mkdir("A/newDir/subDir"); fakeFolder.localModifier().insert("A/newDir/subDir/file", 10); auto expectedState = fakeFolder.currentLocalState(); // Only "A" was modified according to the file system tracker fakeFolder.syncEngine().setLocalDiscoveryOptions( LocalDiscoveryStyle::DatabaseAndFilesystem, { "A" }); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(fakeFolder.currentLocalState(), expectedState); QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } // Tests the behavior of invalid filename detection void testServerBlacklist() { FakeFolder fakeFolder { FileInfo::A12_B12_C12_S12() }; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); fakeFolder.syncEngine().account()->setCapabilities({ { "files", QVariantMap { { "blacklisted_files", QVariantList { ".foo", "bar" } } } } }); fakeFolder.localModifier().insert("C/.foo"); fakeFolder.localModifier().insert("C/bar"); fakeFolder.localModifier().insert("C/moo"); fakeFolder.localModifier().insert("C/.moo"); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find("C/moo")); QVERIFY(fakeFolder.currentRemoteState().find("C/.moo")); QVERIFY(!fakeFolder.currentRemoteState().find("C/.foo")); QVERIFY(!fakeFolder.currentRemoteState().find("C/bar")); } void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedDoNotExist_renameAndUploadFile() { FakeFolder fakeFolder{FileInfo{}}; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const QString fileWithSpaces1(" foo"); const QString fileWithSpaces2(" bar "); const QString fileWithSpaces3("bla "); const QString fileWithSpaces4("A/ foo"); const QString fileWithSpaces5("A/ bar "); const QString fileWithSpaces6("A/bla "); fakeFolder.localModifier().insert(fileWithSpaces1); fakeFolder.localModifier().insert(fileWithSpaces2); fakeFolder.localModifier().insert(fileWithSpaces3); fakeFolder.localModifier().mkdir("A"); fakeFolder.localModifier().insert(fileWithSpaces4); fakeFolder.localModifier().insert(fileWithSpaces5); fakeFolder.localModifier().insert(fileWithSpaces6); fakeFolder.localModifier().mkdir(QStringLiteral(" with spaces ")); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces1.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces1)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces2.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces2)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces3.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces3)); QVERIFY(fakeFolder.currentLocalState().find("A/foo")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentLocalState().find("A/bar")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentLocalState().find("A/bla")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6)); QVERIFY(fakeFolder.currentLocalState().find(QStringLiteral("with spaces"))); QVERIFY(!fakeFolder.currentLocalState().find(QStringLiteral(" with spaces "))); fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem, {QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("bla"), QStringLiteral("A/foo"), QStringLiteral("A/bar"), QStringLiteral("A/bla")}); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces1.trimmed())); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces1)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces1.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces1)); QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces2.trimmed())); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces2)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces2.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces2)); QVERIFY(fakeFolder.currentRemoteState().find(fileWithSpaces3.trimmed())); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces3)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces3.trimmed())); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces3)); QVERIFY(fakeFolder.currentRemoteState().find("A/foo")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentLocalState().find("A/foo")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentRemoteState().find("A/bar")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentLocalState().find("A/bar")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentRemoteState().find("A/bla")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6)); QVERIFY(fakeFolder.currentLocalState().find("A/bla")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6)); QVERIFY(fakeFolder.currentRemoteState().find(QStringLiteral("with spaces"))); QVERIFY(!fakeFolder.currentRemoteState().find(QStringLiteral(" with spaces "))); QVERIFY(fakeFolder.currentLocalState().find(QStringLiteral("with spaces"))); QVERIFY(!fakeFolder.currentLocalState().find(QStringLiteral(" with spaces "))); } void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedDoNotExist_renameFile() { FakeFolder fakeFolder{FileInfo{}}; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const QString fileWithSpaces4("A/ foo"); const QString fileWithSpaces5("A/ bar "); const QString fileWithSpaces6("A/bla "); fakeFolder.remoteModifier().mkdir("A"); fakeFolder.remoteModifier().insert(fileWithSpaces4); fakeFolder.remoteModifier().insert(fileWithSpaces5); fakeFolder.remoteModifier().insert(fileWithSpaces6); qDebug() << fakeFolder.currentRemoteState(); QVERIFY(fakeFolder.syncOnce()); qDebug() << fakeFolder.currentRemoteState(); QVERIFY(fakeFolder.currentRemoteState().find("A/foo")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentRemoteState().find("A/bar")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentRemoteState().find("A/bla")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6)); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find("A/foo")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentLocalState().find("A/foo")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces4)); QVERIFY(fakeFolder.currentRemoteState().find("A/bar")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentLocalState().find("A/bar")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces5)); QVERIFY(fakeFolder.currentRemoteState().find("A/bla")); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces6)); QVERIFY(fakeFolder.currentLocalState().find("A/bla")); QVERIFY(!fakeFolder.currentLocalState().find(fileWithSpaces6)); auto expectedState = fakeFolder.currentLocalState(); QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } void testCreateFileWithTrailingSpaces_localTrimmedDoesExist_dontRenameAndUploadFile() { FakeFolder fakeFolder{FileInfo{}}; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const QString fileWithSpaces(" foo"); const QString fileTrimmed("foo"); fakeFolder.localModifier().insert(fileTrimmed); QVERIFY(fakeFolder.syncOnce()); fakeFolder.localModifier().insert(fileWithSpaces); QVERIFY(!fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed)); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces)); QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed)); } void testCreateFileWithTrailingSpaces_localTrimmedAlsoCreated_dontRenameAndUploadFile() { FakeFolder fakeFolder{FileInfo{}}; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const QString fileWithSpaces(" foo"); const QString fileTrimmed("foo"); fakeFolder.localModifier().insert(fileTrimmed); fakeFolder.localModifier().insert(fileWithSpaces); QVERIFY(!fakeFolder.syncOnce()); QVERIFY(fakeFolder.currentRemoteState().find(fileTrimmed)); QVERIFY(!fakeFolder.currentRemoteState().find(fileWithSpaces)); QVERIFY(fakeFolder.currentLocalState().find(fileWithSpaces)); QVERIFY(fakeFolder.currentLocalState().find(fileTrimmed)); } void testCreateFileWithTrailingSpaces_localAndRemoteTrimmedExists_renameFile() { FakeFolder fakeFolder{FileInfo{}}; QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); const QString fileWithSpaces1(" foo"); const QString fileWithSpaces2(" bar "); const QString fileWithSpaces3("bla "); fakeFolder.localModifier().insert(fileWithSpaces1); fakeFolder.localModifier().insert(fileWithSpaces2); fakeFolder.localModifier().insert(fileWithSpaces3); fakeFolder.remoteModifier().insert(fileWithSpaces1); fakeFolder.remoteModifier().insert(fileWithSpaces2); fakeFolder.remoteModifier().insert(fileWithSpaces3); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.syncOnce()); QVERIFY(fakeFolder.syncOnce()); auto expectedState = fakeFolder.currentLocalState(); qDebug() << expectedState; QCOMPARE(fakeFolder.currentRemoteState(), expectedState); } void testBlockInvalidMtimeSyncRemote() { constexpr auto INVALID_MODTIME1 = 0; constexpr auto INVALID_MODTIME2 = -3600; FakeFolder fakeFolder{FileInfo{}}; 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()); 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); QVERIFY(fakeFolder.syncOnce()); QCOMPARE(nGET, 0); 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()); 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()); 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); } void testDiscoverLockChanges() { FakeFolder fakeFolder{FileInfo{}}; fakeFolder.syncEngine().account()->setCapabilities({{"activity", QVariantMap{{"apiv2", QVariantList{"filters", "filters-api", "previews", "rich-strings"}}}}, {"bruteforce", QVariantMap{{"delay", 0}}}, {"core", QVariantMap{{"pollinterval", 60}, {"webdav-root", "remote.php/webdav"}}}, {"dav", QVariantMap{{"chunking", "1.0"}}}, {"files", QVariantMap{{"bigfilechunking", true}, {"blacklisted_files", QVariantList{".htaccess"}}, {"comments", true}, {"directEditing", QVariantMap{{"etag", "c748e8fc588b54fc5af38c4481a19d20"}, {"url", "https://nextcloud.local/ocs/v2.php/apps/files/api/v1/directEditing"}}}, {"locking", "1.0"}}}}); 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().find("bar")->extraDavProperties = "1" "0" "user1" "user1" "user1" "1648046707"; 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().find("bar")->extraDavProperties = "0"; fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem); QVERIFY(fakeFolder.syncOnce()); } }; QTEST_GUILESS_MAIN(TestLocalDiscovery) #include "testlocaldiscovery.moc"