test that files soon to be expired will be synced automatically
authorMatthieu Gallien <matthieu.gallien@nextcloud.com>
Fri, 7 Oct 2022 16:15:13 +0000 (18:15 +0200)
committerMatthieu Gallien <matthieu_gallien@yahoo.fr>
Tue, 11 Oct 2022 14:01:58 +0000 (16:01 +0200)
try to ensure that we properly sync again files for which lock has
expired

Signed-off-by: Matthieu Gallien <matthieu.gallien@nextcloud.com>
test/syncenginetestutils.cpp
test/syncenginetestutils.h
test/testlocaldiscovery.cpp
test/testlockfile.cpp

index 70b87bb5a9d6f58fae1ad0f21196981aed72ff2e..f7e1faf5abb61a15e9ebe10d94ab5faa41d5adab 100644 (file)
@@ -103,6 +103,10 @@ void DiskFileModifier::setModTime(const QString &relativePath, const QDateTime &
     OCC::FileSystem::setModTime(_rootDir.filePath(relativePath), OCC::Utility::qDateTimeToTime_t(modTime));
 }
 
+void DiskFileModifier::modifyLockState([[maybe_unused]] const QString &relativePath, [[maybe_unused]] LockState lockState, [[maybe_unused]] int lockType, [[maybe_unused]] const QString &lockOwner, [[maybe_unused]] const QString &lockOwnerId, [[maybe_unused]] const QString &lockEditorId, [[maybe_unused]] quint64 lockTime, [[maybe_unused]] quint64 lockTimeout)
+{
+}
+
 FileInfo FileInfo::A12_B12_C12_S12()
 {
     FileInfo fi { QString {}, {
@@ -195,6 +199,19 @@ void FileInfo::setModTimeKeepEtag(const QString &relativePath, const QDateTime &
     file->lastModified = modTime;
 }
 
+void FileInfo::modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout)
+{
+    FileInfo *file = findInvalidatingEtags(relativePath);
+    Q_ASSERT(file);
+    file->lockState = lockState;
+    file->lockType = lockType;
+    file->lockOwner = lockOwner;
+    file->lockOwnerId = lockOwnerId;
+    file->lockEditorId = lockEditorId;
+    file->lockTime = lockTime;
+    file->lockTimeout = lockTimeout;
+}
+
 FileInfo *FileInfo::find(PathComponents pathComponents, const bool invalidateEtags)
 {
     if (pathComponents.isEmpty()) {
@@ -334,6 +351,13 @@ FakePropfindReply::FakePropfindReply(FileInfo &remoteRootFileInfo, QNetworkAcces
         xml.writeTextElement(ocUri, QStringLiteral("permissions"), !fileInfo.permissions.isNull() ? QString(fileInfo.permissions.toString()) : fileInfo.isShared ? QStringLiteral("SRDNVCKW") : QStringLiteral("RDNVCKW"));
         xml.writeTextElement(ocUri, QStringLiteral("id"), QString::fromUtf8(fileInfo.fileId));
         xml.writeTextElement(ocUri, QStringLiteral("checksums"), QString::fromUtf8(fileInfo.checksums));
+        xml.writeTextElement(ncUri, QStringLiteral("lock-owner"), fileInfo.lockOwnerId);
+        xml.writeTextElement(ncUri, QStringLiteral("lock"), fileInfo.lockState == FileInfo::LockState::FileLocked ? QStringLiteral("1") : QStringLiteral("0"));
+        xml.writeTextElement(ncUri, QStringLiteral("lock-owner-type"), fileInfo.lockOwnerId);
+        xml.writeTextElement(ncUri, QStringLiteral("lock-owner-displayname"), fileInfo.lockOwnerId);
+        xml.writeTextElement(ncUri, QStringLiteral("lock-owner-editor"), fileInfo.lockOwnerId);
+        xml.writeTextElement(ncUri, QStringLiteral("lock-time"), QString::number(fileInfo.lockTime));
+        xml.writeTextElement(ncUri, QStringLiteral("lock-timeout"), QString::number(fileInfo.lockTimeout));
         buffer.write(fileInfo.extraDavProperties);
         xml.writeEndElement(); // prop
         xml.writeTextElement(davUri, QStringLiteral("status"), QStringLiteral("HTTP/1.1 200 OK"));
index ddd50273bbb7e82a92c8e69a5b52ddb3e3a07681..ab4074df6155c98c654143c3de0ad5a612d1b5ca 100644 (file)
@@ -76,6 +76,11 @@ public:
 class FileModifier
 {
 public:
+    enum class LockState {
+        FileLocked,
+        FileUnlocked,
+    };
+
     virtual ~FileModifier() = default;
     virtual void remove(const QString &relativePath) = 0;
     virtual void insert(const QString &relativePath, qint64 size = 64, char contentChar = 'W') = 0;
@@ -84,6 +89,7 @@ public:
     virtual void mkdir(const QString &relativePath) = 0;
     virtual void rename(const QString &relativePath, const QString &relativeDestinationDirectory) = 0;
     virtual void setModTime(const QString &relativePath, const QDateTime &modTime) = 0;
+    virtual void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) = 0;
 };
 
 class DiskFileModifier : public FileModifier
@@ -99,6 +105,7 @@ public:
     void mkdir(const QString &relativePath) override;
     void rename(const QString &from, const QString &to) override;
     void setModTime(const QString &relativePath, const QDateTime &modTime) override;
+    void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
 };
 
 class FileInfo : public FileModifier
@@ -130,6 +137,8 @@ public:
 
     void setModTimeKeepEtag(const QString &relativePath, const QDateTime &modTime);
 
+    void modifyLockState(const QString &relativePath, LockState lockState, int lockType, const QString &lockOwner, const QString &lockOwnerId, const QString &lockEditorId, quint64 lockTime, quint64 lockTimeout) override;
+
     FileInfo *find(PathComponents pathComponents, const bool invalidateEtags = false);
 
     FileInfo *createDir(const QString &relativePath);
@@ -163,6 +172,13 @@ public:
     QByteArray extraDavProperties;
     qint64 size = 0;
     char contentChar = 'W';
+    LockState lockState = LockState::FileUnlocked;
+    int lockType = 0;
+    QString lockOwner;
+    QString lockOwnerId;
+    QString lockEditorId;
+    quint64 lockTime = 0;
+    quint64 lockTimeout = 0;
 
     // Sorted by name to be able to compare trees
     QMap<QString, FileInfo> children;
index 80350bf907827addbaeb13f9e7340c8824a0134b..9ed191c8e8cb3c0f6c79d006a92c0e41c6c5140b 100644 (file)
@@ -621,15 +621,7 @@ private slots:
 
         fakeFolder.remoteModifier().insert(fooFileRootFolder);
         fakeFolder.remoteModifier().insert(barFileRootFolder);
-
-        const auto lockedFileDavProps = QByteArray("<nc:lock>1</nc:lock>"
-                                                   "<nc:lock-owner-type>0</nc:lock-owner-type>"
-                                                   "<nc:lock-owner>user1</nc:lock-owner>"
-                                                   "<nc:lock-owner-displayname>user1</nc:lock-owner-displayname>"
-                                                   "<nc:lock-owner-editor>user1</nc:lock-owner-editor>"
-                                                   "<nc:lock-time>1648046707</nc:lock-time>");
-
-        fakeFolder.remoteModifier().find("bar")->extraDavProperties = lockedFileDavProps;
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileLocked, 0, QStringLiteral("user1"), {}, QStringLiteral("user1"), 1648046707, 0);
 
         fakeFolder.remoteModifier().mkdir(QStringLiteral("subfolder"));
         fakeFolder.remoteModifier().insert(fooFileSubFolder);
@@ -648,8 +640,7 @@ private slots:
         QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("bar"), &fileRecordBefore));
         QVERIFY(fileRecordBefore._lockstate._locked);
 
-        const auto unlockedFileDavProps = QByteArray("<nc:lock>0</nc:lock>");
-        fakeFolder.remoteModifier().find("bar")->extraDavProperties = unlockedFileDavProps;
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("bar"), FileInfo::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
 
         fakeFolder.syncEngine().setLocalDiscoveryOptions(LocalDiscoveryStyle::DatabaseAndFilesystem);
 
index 97538295e5b4ff57c44a9024511ff132e07f529c..16f246fcfa0406991e78e34f9c26ed4449f7cb87 100644 (file)
@@ -5,6 +5,7 @@
 #include "common/syncjournaldb.h"
 #include "common/syncjournalfilerecord.h"
 #include "syncenginetestutils.h"
+#include "localdiscoverytracker.h"
 
 #include <QTest>
 #include <QSignalSpy>
@@ -482,6 +483,93 @@ private slots:
         QVERIFY(jobFailure.wait());
         QCOMPARE(jobSuccess.count(), 0);
     }
+
+    void testSyncLockedFilesAlmostExpired()
+    {
+        FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished);
+
+        ItemCompletedSpy completeSpy(fakeFolder);
+
+        completeSpy.clear();
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem);
+        OCC::SyncJournalFileRecord fileRecordBefore;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore));
+        QVERIFY(!fileRecordBefore._lockstate._locked);
+
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226);
+
+        completeSpy.clear();
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem);
+        OCC::SyncJournalFileRecord fileRecordLocked;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked));
+        QVERIFY(fileRecordLocked._lockstate._locked);
+
+        spySyncCompleted.clear();
+
+        QTest::qWait(5000);
+
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
+
+        QCOMPARE(spySyncCompleted.count(), 0);
+        QVERIFY(spySyncCompleted.wait(3000));
+        QCOMPARE(spySyncCompleted.count(), 1);
+
+        OCC::SyncJournalFileRecord fileRecordUnlocked;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked));
+        QVERIFY(!fileRecordUnlocked._lockstate._locked);
+    }
+
+    void testSyncLockedFilesNoExpiredLockedFiles()
+    {
+        FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
+        QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+        QSignalSpy spySyncCompleted(&fakeFolder.syncEngine(), &OCC::SyncEngine::finished);
+
+        ItemCompletedSpy completeSpy(fakeFolder);
+
+        completeSpy.clear();
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::UnlockedItem);
+        OCC::SyncJournalFileRecord fileRecordBefore;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordBefore));
+        QVERIFY(!fileRecordBefore._lockstate._locked);
+
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileLocked, 1, QStringLiteral("Nextcloud Office"), {}, QStringLiteral("richdocuments"), QDateTime::currentDateTime().toSecsSinceEpoch() - 1220, 1226);
+
+        completeSpy.clear();
+        QVERIFY(fakeFolder.syncOnce());
+
+        QCOMPARE(completeSpy.findItem(QStringLiteral("A/a1"))->_locked, OCC::SyncFileItem::LockStatus::LockedItem);
+        OCC::SyncJournalFileRecord fileRecordLocked;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordLocked));
+        QVERIFY(fileRecordLocked._lockstate._locked);
+
+        completeSpy.clear();
+        QVERIFY(fakeFolder.syncOnce());
+
+        spySyncCompleted.clear();
+
+        QTest::qWait(1000);
+
+        fakeFolder.remoteModifier().modifyLockState(QStringLiteral("A/a1"), FileModifier::LockState::FileUnlocked, {}, {}, {}, {}, {}, {});
+
+        QCOMPARE(spySyncCompleted.count(), 0);
+        QVERIFY(!spySyncCompleted.wait(3000));
+        QCOMPARE(spySyncCompleted.count(), 0);
+
+        OCC::SyncJournalFileRecord fileRecordUnlocked;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(QStringLiteral("A/a1"), &fileRecordUnlocked));
+        QVERIFY(fileRecordUnlocked._lockstate._locked);
+    }
 };
 
 QTEST_GUILESS_MAIN(TestLockFile)