mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2025-05-09 23:34:09 +02:00
Pin state updates
- unspecified and inherited are different - move enum to header in common/ - access through Vfs instead of directly in Journal
This commit is contained in:
parent
2722c61515
commit
7f400e3226
76
src/common/pinstate.h
Normal file
76
src/common/pinstate.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) by Christian Kamm <mail@ckamm.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef PINSTATE_H
|
||||
#define PINSTATE_H
|
||||
|
||||
#include "ocsynclib.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/** Determines whether items should be available locally permanently or not
|
||||
*
|
||||
* The idea is that files and folders can be marked with the user intent
|
||||
* on availability.
|
||||
*
|
||||
* The Inherited state is used for resetting a pin state to what its
|
||||
* parent path would do.
|
||||
*
|
||||
* The pin state of a directory usually only matters for the initial pin and
|
||||
* hydration state of new remote files. It's perfectly possible for a
|
||||
* AlwaysLocal directory to have only OnlineOnly items. (though setting pin
|
||||
* states is usually done recursively, so one'd need to set the folder to
|
||||
* pinned and then each contained item to unpinned)
|
||||
*
|
||||
* Note: This enum intentionally mimics CF_PIN_STATE of Windows cfapi.
|
||||
*/
|
||||
enum class PinState {
|
||||
/** The pin state is derived from the state of the parent folder.
|
||||
*
|
||||
* For example new remote files start out in this state, following
|
||||
* the state of their parent folder.
|
||||
*
|
||||
* This state is used purely for resetting pin states to their derived
|
||||
* value. The effective state for an item will never be "Inherited".
|
||||
*/
|
||||
Inherited = 0,
|
||||
|
||||
/** The file shall be available and up to date locally.
|
||||
*
|
||||
* Also known as "pinned". Pinned dehydrated files shall be hydrated
|
||||
* as soon as possible.
|
||||
*/
|
||||
AlwaysLocal = 1,
|
||||
|
||||
/** File shall be a dehydrated placeholder, filled on demand.
|
||||
*
|
||||
* Also known as "unpinned". Unpinned hydrated files shall be dehydrated
|
||||
* as soon as possible.
|
||||
*
|
||||
* If a unpinned file becomes hydrated its pin state changes to unspecified.
|
||||
*/
|
||||
OnlineOnly = 2,
|
||||
|
||||
/** The user hasn't made a decision. The client or platform may hydrate or
|
||||
* dehydrate as they see fit.
|
||||
*
|
||||
* New remote files in unspecified directories start unspecified, and
|
||||
* dehydrated (which is an arbitrary decision).
|
||||
*/
|
||||
Unspecified = 3,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -29,28 +29,11 @@
|
||||
#include "common/ownsql.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
#include "common/result.h"
|
||||
#include "common/pinstate.h"
|
||||
|
||||
namespace OCC {
|
||||
class SyncJournalFileRecord;
|
||||
|
||||
/** Determines whether files should be available locally or not
|
||||
*
|
||||
* For new remote files the file's PinState is calculated by looking for
|
||||
* the closest parent folder that isn't Inherited.
|
||||
*
|
||||
* TODO: It seems to make sense to also store per-file PinStates.
|
||||
* Maybe these could communicate intent, similar to ItemTypeVirtualFileDownload
|
||||
* and ...FileDehydrate?
|
||||
*/
|
||||
enum class PinState {
|
||||
/// Inherit the PinState of the parent directory (default)
|
||||
Inherited = 0,
|
||||
/// Download file and keep it updated.
|
||||
AlwaysLocal = 1,
|
||||
/// File shall be virtual locally.
|
||||
OnlineOnly = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class that handles the sync database
|
||||
*
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "vfs.h"
|
||||
#include "plugin.h"
|
||||
#include "version.h"
|
||||
#include "syncjournaldb.h"
|
||||
|
||||
#include <QPluginLoader>
|
||||
#include <QLoggingCategory>
|
||||
@ -59,11 +60,34 @@ Optional<Vfs::Mode> Vfs::modeFromString(const QString &str)
|
||||
return {};
|
||||
}
|
||||
|
||||
VfsOff::VfsOff(QObject *parent)
|
||||
VfsDefaults::VfsDefaults(QObject *parent)
|
||||
: Vfs(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void VfsDefaults::start(const VfsSetupParams ¶ms)
|
||||
{
|
||||
_setupParams = params;
|
||||
}
|
||||
|
||||
bool VfsDefaults::setPinState(const QString &folderPath, PinState state)
|
||||
{
|
||||
auto path = folderPath.toUtf8();
|
||||
_setupParams.journal->wipePinStateForPathAndBelow(path);
|
||||
_setupParams.journal->setPinStateForPath(path, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
Optional<PinState> VfsDefaults::getPinState(const QString &folderPath)
|
||||
{
|
||||
return _setupParams.journal->effectivePinStateForPath(folderPath.toUtf8());
|
||||
}
|
||||
|
||||
VfsOff::VfsOff(QObject *parent)
|
||||
: VfsDefaults(parent)
|
||||
{
|
||||
}
|
||||
|
||||
VfsOff::~VfsOff() = default;
|
||||
|
||||
static QString modeToPluginName(Vfs::Mode mode)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "ocsynclib.h"
|
||||
#include "result.h"
|
||||
#include "syncfilestatus.h"
|
||||
#include "pinstate.h"
|
||||
|
||||
typedef struct csync_file_stat_s csync_file_stat_t;
|
||||
|
||||
@ -48,7 +49,7 @@ struct OCSYNC_EXPORT VfsSetupParams
|
||||
*
|
||||
* Note: The journal must live at least until the Vfs::stop() call.
|
||||
*/
|
||||
SyncJournalDb *journal;
|
||||
SyncJournalDb *journal = nullptr;
|
||||
|
||||
/// Strings potentially passed on to the platform
|
||||
QString providerName;
|
||||
@ -101,14 +102,14 @@ public:
|
||||
virtual QString fileSuffix() const = 0;
|
||||
|
||||
|
||||
/// Must be called at least once before start(). May make sense to merge with start().
|
||||
virtual void registerFolder(const VfsSetupParams ¶ms) = 0;
|
||||
|
||||
/** Initializes interaction with the VFS provider.
|
||||
*
|
||||
* For example, the VFS provider might monitor files to be able to start a file
|
||||
* hydration (download of a file's remote contents) when the user wants to open
|
||||
* it.
|
||||
*
|
||||
* Usually some registration needs to be done with the backend. This function
|
||||
* should take care of it if necessary.
|
||||
*/
|
||||
virtual void start(const VfsSetupParams ¶ms) = 0;
|
||||
|
||||
@ -160,6 +161,24 @@ public:
|
||||
*/
|
||||
virtual bool statTypeVirtualFile(csync_file_stat_t *stat, void *stat_data) = 0;
|
||||
|
||||
/** Sets the pin state for the item at a path.
|
||||
*
|
||||
* Usually this would forward to setting the pin state flag in the db table,
|
||||
* but some vfs plugins will store the pin state in file attributes instead.
|
||||
*
|
||||
* folderPath is relative to the sync folder.
|
||||
*/
|
||||
virtual bool setPinState(const QString &folderPath, PinState state) = 0;
|
||||
|
||||
/** Returns the pin state of an item at a path.
|
||||
*
|
||||
* Usually backed by the db's effectivePinState() function but some vfs
|
||||
* plugins will override it to retrieve the state from elsewhere.
|
||||
*
|
||||
* folderPath is relative to the sync folder.
|
||||
*/
|
||||
virtual Optional<PinState> getPinState(const QString &folderPath) = 0;
|
||||
|
||||
public slots:
|
||||
/** Update in-sync state based on SyncFileStatusTracker signal.
|
||||
*
|
||||
@ -176,8 +195,27 @@ signals:
|
||||
void doneHydrating();
|
||||
};
|
||||
|
||||
class OCSYNC_EXPORT VfsDefaults : public Vfs
|
||||
{
|
||||
public:
|
||||
explicit VfsDefaults(QObject* parent = nullptr);
|
||||
|
||||
// stores the params
|
||||
void start(const VfsSetupParams ¶ms) override;
|
||||
|
||||
// use the journal to back the pinstates
|
||||
bool setPinState(const QString &folderPath, PinState state) override;
|
||||
Optional<PinState> getPinState(const QString &folderPath) override;
|
||||
|
||||
// access initial setup data
|
||||
const VfsSetupParams ¶ms() const { return _setupParams; }
|
||||
|
||||
protected:
|
||||
VfsSetupParams _setupParams;
|
||||
};
|
||||
|
||||
/// Implementation of Vfs for Vfs::Off mode - does nothing
|
||||
class OCSYNC_EXPORT VfsOff : public Vfs
|
||||
class OCSYNC_EXPORT VfsOff : public VfsDefaults
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -189,12 +227,9 @@ public:
|
||||
|
||||
QString fileSuffix() const override { return QString(); }
|
||||
|
||||
void registerFolder(const VfsSetupParams &) override {}
|
||||
void start(const VfsSetupParams &) override {}
|
||||
void stop() override {}
|
||||
void unregisterFolder() override {}
|
||||
|
||||
|
||||
bool isHydrating() const override { return false; }
|
||||
|
||||
bool updateMetadata(const QString &, time_t, quint64, const QByteArray &, QString *) override { return true; }
|
||||
|
@ -487,7 +487,6 @@ void Folder::startVfs()
|
||||
connect(&_engine->syncFileStatusTracker(), &SyncFileStatusTracker::fileStatusChanged,
|
||||
_vfs.data(), &Vfs::fileStatusChanged);
|
||||
|
||||
_vfs->registerFolder(vfsParams); // Do this always?
|
||||
_vfs->start(vfsParams);
|
||||
}
|
||||
|
||||
|
@ -205,6 +205,7 @@ public:
|
||||
// Used by the Socket API
|
||||
SyncJournalDb *journalDb() { return &_journal; }
|
||||
SyncEngine &syncEngine() { return *_engine; }
|
||||
Vfs &vfs() { return *_vfs; }
|
||||
|
||||
RequestEtagJob *etagJob() { return _requestEtagJob; }
|
||||
std::chrono::milliseconds msecSinceLastSync() const { return std::chrono::milliseconds(_timeSinceLastSyncDone.elapsed()); }
|
||||
|
@ -69,7 +69,10 @@ void WatcherThread::watchChanges(size_t fileNotifyBufferSize,
|
||||
SecureZeroMemory(pFileNotifyBuffer, fileNotifyBufferSize);
|
||||
if (!ReadDirectoryChangesW(_directory, (LPVOID)pFileNotifyBuffer,
|
||||
OCC::Utility::convertSizeToDWORD(fileNotifyBufferSize), true,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME
|
||||
| FILE_NOTIFY_CHANGE_DIR_NAME
|
||||
| FILE_NOTIFY_CHANGE_LAST_WRITE
|
||||
| FILE_NOTIFY_CHANGE_ATTRIBUTES, // attributes are for vfs pin state changes
|
||||
&dwBytesReturned,
|
||||
&overlapped,
|
||||
nullptr)) {
|
||||
|
@ -700,9 +700,8 @@ void SocketApi::command_MAKE_AVAILABLE_LOCALLY(const QString &filesArg, SocketLi
|
||||
continue;
|
||||
|
||||
// Update the pin state on all items
|
||||
auto pinPath = data.folderRelativePathNoVfsSuffix().toUtf8();
|
||||
data.folder->journalDb()->wipePinStateForPathAndBelow(pinPath);
|
||||
data.folder->journalDb()->setPinStateForPath(pinPath, PinState::AlwaysLocal);
|
||||
auto pinPath = data.folderRelativePathNoVfsSuffix();
|
||||
data.folder->vfs().setPinState(pinPath, PinState::AlwaysLocal);
|
||||
|
||||
// Trigger the recursive download
|
||||
data.folder->downloadVirtualFile(data.folderRelativePath);
|
||||
@ -720,9 +719,8 @@ void SocketApi::command_MAKE_ONLINE_ONLY(const QString &filesArg, SocketListener
|
||||
continue;
|
||||
|
||||
// Update the pin state on all items
|
||||
auto pinPath = data.folderRelativePathNoVfsSuffix().toUtf8();
|
||||
data.folder->journalDb()->wipePinStateForPathAndBelow(pinPath);
|
||||
data.folder->journalDb()->setPinStateForPath(pinPath, PinState::OnlineOnly);
|
||||
auto pinPath = data.folderRelativePathNoVfsSuffix();
|
||||
data.folder->vfs().setPinState(pinPath, PinState::OnlineOnly);
|
||||
|
||||
// Trigger recursive dehydration
|
||||
data.folder->dehydrateFile(data.folderRelativePath);
|
||||
@ -1021,7 +1019,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
|
||||
for (const auto &file : files) {
|
||||
auto fileData = FileData::get(file);
|
||||
auto path = fileData.folderRelativePathNoVfsSuffix();
|
||||
auto pinState = syncFolder->journalDb()->effectivePinStateForPath(path.toUtf8());
|
||||
auto pinState = syncFolder->vfs().getPinState(path);
|
||||
if (!pinState) {
|
||||
// db error
|
||||
hasAlwaysLocal = true;
|
||||
|
@ -426,14 +426,10 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo(
|
||||
}
|
||||
// Turn new remote files into virtual files if the option is enabled.
|
||||
auto &opts = _discoveryData->_syncOptions;
|
||||
if (!directoryPinState()) {
|
||||
dbError();
|
||||
return;
|
||||
}
|
||||
if (!localEntry.isValid()
|
||||
&& item->_type == ItemTypeFile
|
||||
&& opts._vfs->mode() != Vfs::Off
|
||||
&& *directoryPinState() == PinState::OnlineOnly) {
|
||||
&& _pinState != PinState::AlwaysLocal) {
|
||||
item->_type = ItemTypeVirtualFile;
|
||||
if (isVfsWithSuffix())
|
||||
addVirtualFileSuffix(path._original);
|
||||
@ -989,8 +985,7 @@ void ProcessDirectoryJob::processFileFinalize(
|
||||
if (!checkPermissions(item))
|
||||
recurse = false;
|
||||
if (recurse) {
|
||||
auto job = new ProcessDirectoryJob(item, recurseQueryLocal, recurseQueryServer, _discoveryData, this);
|
||||
job->_currentFolder = path;
|
||||
auto job = new ProcessDirectoryJob(path, item, recurseQueryLocal, recurseQueryServer, this);
|
||||
if (item->_instruction == CSYNC_INSTRUCTION_REMOVE) {
|
||||
job->setParent(_discoveryData);
|
||||
_discoveryData->_queuedDeletedDirectories[path._original] = job;
|
||||
@ -1031,8 +1026,7 @@ void ProcessDirectoryJob::processBlacklisted(const PathTuple &path, const OCC::L
|
||||
qCInfo(lcDisco) << "Discovered (blacklisted) " << item->_file << item->_instruction << item->_direction << item->isDirectory();
|
||||
|
||||
if (item->isDirectory() && item->_instruction != CSYNC_INSTRUCTION_IGNORE) {
|
||||
auto job = new ProcessDirectoryJob(item, NormalQuery, InBlackList, _discoveryData, this);
|
||||
job->_currentFolder = path;
|
||||
auto job = new ProcessDirectoryJob(path, item, NormalQuery, InBlackList, this);
|
||||
connect(job, &ProcessDirectoryJob::finished, this, &ProcessDirectoryJob::subJobFinished);
|
||||
_queuedJobs.push_back(job);
|
||||
} else {
|
||||
@ -1357,19 +1351,18 @@ bool ProcessDirectoryJob::runLocalQuery()
|
||||
return true;
|
||||
}
|
||||
|
||||
Optional<PinState> ProcessDirectoryJob::directoryPinState()
|
||||
{
|
||||
if (!_pinStateCache) {
|
||||
_pinStateCache = _discoveryData->_statedb->effectivePinStateForPath(
|
||||
_currentFolder._original.toUtf8());
|
||||
// don't cache db errors, just retry next time
|
||||
}
|
||||
return _pinStateCache;
|
||||
}
|
||||
|
||||
bool ProcessDirectoryJob::isVfsWithSuffix() const
|
||||
{
|
||||
return _discoveryData->_syncOptions._vfs->mode() == Vfs::WithSuffix;
|
||||
}
|
||||
|
||||
void ProcessDirectoryJob::computePinState(PinState parentState)
|
||||
{
|
||||
_pinState = parentState;
|
||||
if (_queryLocal != ParentDontExist) {
|
||||
if (auto state = _discoveryData->_syncOptions._vfs->getPinState(_currentFolder._local)) // ouch! pin local or original?
|
||||
_pinState = *state;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ class SyncJournalDb;
|
||||
class ProcessDirectoryJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
struct PathTuple;
|
||||
public:
|
||||
enum QueryMode {
|
||||
NormalQuery,
|
||||
@ -56,14 +58,30 @@ public:
|
||||
InBlackList // Do not query this folder because it is in the blacklist (remote entries only)
|
||||
};
|
||||
Q_ENUM(QueryMode)
|
||||
explicit ProcessDirectoryJob(const SyncFileItemPtr &dirItem, QueryMode queryLocal, QueryMode queryServer,
|
||||
DiscoveryPhase *data, QObject *parent)
|
||||
|
||||
/** For creating the root job
|
||||
*
|
||||
* The base pin state is used if the root dir's pin state can't be retrieved.
|
||||
*/
|
||||
explicit ProcessDirectoryJob(DiscoveryPhase *data, PinState basePinState, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _discoveryData(data)
|
||||
{
|
||||
computePinState(basePinState);
|
||||
}
|
||||
|
||||
/// For creating subjobs
|
||||
explicit ProcessDirectoryJob(const PathTuple &path, const SyncFileItemPtr &dirItem,
|
||||
QueryMode queryLocal, QueryMode queryServer,
|
||||
ProcessDirectoryJob *parent)
|
||||
: QObject(parent)
|
||||
, _dirItem(dirItem)
|
||||
, _queryServer(queryServer)
|
||||
, _queryLocal(queryLocal)
|
||||
, _discoveryData(data)
|
||||
, _discoveryData(parent->_discoveryData)
|
||||
, _currentFolder(path)
|
||||
{
|
||||
computePinState(parent->_pinState);
|
||||
}
|
||||
|
||||
void start();
|
||||
@ -180,11 +198,15 @@ private:
|
||||
*/
|
||||
bool runLocalQuery();
|
||||
|
||||
/** Retrieve and cache directory pin state */
|
||||
Optional<PinState> directoryPinState();
|
||||
/** Sets _pinState
|
||||
*
|
||||
* If the folder exists locally its state is retrieved, otherwise the
|
||||
* parent's pin state is inherited.
|
||||
*/
|
||||
void computePinState(PinState parentState);
|
||||
|
||||
QueryMode _queryServer;
|
||||
QueryMode _queryLocal;
|
||||
QueryMode _queryServer = QueryMode::NormalQuery;
|
||||
QueryMode _queryLocal = QueryMode::NormalQuery;
|
||||
|
||||
// Holds entries that resulted from a NormalQuery
|
||||
QVector<RemoteInfo> _serverNormalQueryEntries;
|
||||
@ -222,7 +244,7 @@ private:
|
||||
PathTuple _currentFolder;
|
||||
bool _childModified = false; // the directory contains modified item what would prevent deletion
|
||||
bool _childIgnored = false; // The directory contains ignored item that would prevent deletion
|
||||
Optional<PinState> _pinStateCache; // The directories pin-state, once retrieved, see directoryPinState()
|
||||
PinState _pinState = PinState::Unspecified; // The directory's pin-state, see setParentPinState()
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
@ -637,8 +637,8 @@ void SyncEngine::slotStartDiscovery()
|
||||
connect(_discoveryPhase.data(), &DiscoveryPhase::silentlyExcluded,
|
||||
_syncFileStatusTracker.data(), &SyncFileStatusTracker::slotAddSilentlyExcluded);
|
||||
|
||||
auto discoveryJob = new ProcessDirectoryJob(SyncFileItemPtr(), ProcessDirectoryJob::NormalQuery, ProcessDirectoryJob::NormalQuery,
|
||||
_discoveryPhase.data(), _discoveryPhase.data());
|
||||
auto discoveryJob = new ProcessDirectoryJob(
|
||||
_discoveryPhase.data(), PinState::AlwaysLocal, _discoveryPhase.data());
|
||||
_discoveryPhase->startJob(discoveryJob);
|
||||
connect(discoveryJob, &ProcessDirectoryJob::etag, this, &SyncEngine::slotRootEtagReceived);
|
||||
}
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace OCC {
|
||||
|
||||
VfsSuffix::VfsSuffix(QObject *parent)
|
||||
: Vfs(parent)
|
||||
: VfsDefaults(parent)
|
||||
{
|
||||
}
|
||||
|
||||
@ -40,14 +40,6 @@ QString VfsSuffix::fileSuffix() const
|
||||
return QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
|
||||
}
|
||||
|
||||
void VfsSuffix::registerFolder(const VfsSetupParams &)
|
||||
{
|
||||
}
|
||||
|
||||
void VfsSuffix::start(const VfsSetupParams &)
|
||||
{
|
||||
}
|
||||
|
||||
void VfsSuffix::stop()
|
||||
{
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class VfsSuffix : public Vfs
|
||||
class VfsSuffix : public VfsDefaults
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -32,8 +32,6 @@ public:
|
||||
Mode mode() const override;
|
||||
QString fileSuffix() const override;
|
||||
|
||||
void registerFolder(const VfsSetupParams ¶ms) override;
|
||||
void start(const VfsSetupParams ¶ms) override;
|
||||
void stop() override;
|
||||
void unregisterFolder() override;
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "filesystem.h"
|
||||
#include "syncengine.h"
|
||||
#include "common/syncjournaldb.h"
|
||||
#include "common/vfs.h"
|
||||
#include "csync_exclude.h"
|
||||
|
||||
#include <QDir>
|
||||
@ -925,12 +926,41 @@ public:
|
||||
// Ignore temporary files from the download. (This is in the default exclude list, but we don't load it)
|
||||
_syncEngine->excludedFiles().addManualExclude("]*.~*");
|
||||
|
||||
// Ensure we have a valid VfsOff instance "running"
|
||||
switchToVfs(_syncEngine->syncOptions()._vfs);
|
||||
|
||||
// A new folder will update the local file state database on first sync.
|
||||
// To have a state matching what users will encounter, we have to a sync
|
||||
// using an identical local/remote file tree first.
|
||||
syncOnce();
|
||||
}
|
||||
|
||||
void switchToVfs(QSharedPointer<OCC::Vfs> vfs)
|
||||
{
|
||||
auto opts = _syncEngine->syncOptions();
|
||||
|
||||
opts._vfs->stop();
|
||||
QObject::disconnect(_syncEngine.get(), 0, opts._vfs.data(), 0);
|
||||
|
||||
opts._vfs = vfs;
|
||||
_syncEngine->setSyncOptions(opts);
|
||||
|
||||
OCC::VfsSetupParams vfsParams;
|
||||
vfsParams.filesystemPath = localPath();
|
||||
vfsParams.remotePath = "";
|
||||
vfsParams.account = _account;
|
||||
vfsParams.journal = _journalDb.get();
|
||||
vfsParams.providerName = "OC-TEST";
|
||||
vfsParams.providerVersion = "0.1";
|
||||
vfsParams.enableShellIntegration = false;
|
||||
QObject::connect(_syncEngine.get(), &QObject::destroyed, vfs.data(), [vfs]() {
|
||||
vfs->stop();
|
||||
vfs->unregisterFolder();
|
||||
});
|
||||
|
||||
vfs->start(vfsParams);
|
||||
}
|
||||
|
||||
OCC::AccountPtr account() const { return _account; }
|
||||
OCC::SyncEngine &syncEngine() const { return *_syncEngine; }
|
||||
OCC::SyncJournalDb &syncJournal() const { return *_journalDb; }
|
||||
|
@ -59,12 +59,15 @@ void markForDehydration(FakeFolder &folder, const QByteArray &path)
|
||||
journal.avoidReadFromDbOnNextSync(record._path);
|
||||
}
|
||||
|
||||
SyncOptions vfsSyncOptions(FakeFolder &fakeFolder)
|
||||
QSharedPointer<Vfs> setupVfs(FakeFolder &folder)
|
||||
{
|
||||
SyncOptions options;
|
||||
options._vfs.reset(createVfsFromPlugin(Vfs::WithSuffix).release());
|
||||
fakeFolder.syncJournal().setPinStateForPath("", PinState::OnlineOnly);
|
||||
return options;
|
||||
auto suffixVfs = QSharedPointer<Vfs>(createVfsFromPlugin(Vfs::WithSuffix).release());
|
||||
folder.switchToVfs(suffixVfs);
|
||||
|
||||
// Using this directly doesn't recursively unpin everything
|
||||
folder.syncJournal().setPinStateForPath("", PinState::OnlineOnly);
|
||||
|
||||
return suffixVfs;
|
||||
}
|
||||
|
||||
class TestSyncVirtualFiles : public QObject
|
||||
@ -85,7 +88,7 @@ private slots:
|
||||
QFETCH(bool, doLocalDiscovery);
|
||||
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -206,7 +209,7 @@ private slots:
|
||||
void testVirtualFileConflict()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -277,7 +280,7 @@ private slots:
|
||||
void testWithNormalSync()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -313,7 +316,7 @@ private slots:
|
||||
void testVirtualFileDownload()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -381,7 +384,7 @@ private slots:
|
||||
void testVirtualFileDownloadResume()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -422,8 +425,7 @@ private slots:
|
||||
void testNewFilesNotVirtual()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
SyncOptions syncOptions = vfsSyncOptions(fakeFolder);
|
||||
fakeFolder.syncEngine().setSyncOptions(syncOptions);
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
|
||||
fakeFolder.remoteModifier().mkdir("A");
|
||||
@ -443,7 +445,7 @@ private slots:
|
||||
void testDownloadRecursive()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
|
||||
// Create a virtual file for remote files
|
||||
@ -540,7 +542,7 @@ private slots:
|
||||
void testRenameToVirtual()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -578,7 +580,7 @@ private slots:
|
||||
void testRenameVirtual()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||
|
||||
@ -620,7 +622,7 @@ private slots:
|
||||
void testSyncDehydration()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
@ -697,7 +699,7 @@ private slots:
|
||||
void testWipeVirtualSuffixFiles()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo{} };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
|
||||
// Create a suffix-vfs baseline
|
||||
|
||||
@ -733,7 +735,7 @@ private slots:
|
||||
QVERIFY(fakeFolder.currentLocalState().find("A/a3.nextcloud"));
|
||||
QVERIFY(!fakeFolder.currentLocalState().find("A/B/b1.nextcloud"));
|
||||
|
||||
fakeFolder.syncEngine().setSyncOptions(SyncOptions{});
|
||||
fakeFolder.switchToVfs(QSharedPointer<Vfs>(new VfsOff));
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QVERIFY(fakeFolder.currentRemoteState().find("A/a3.nextcloud")); // regular upload
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
@ -742,7 +744,7 @@ private slots:
|
||||
void testNewVirtuals()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo() };
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
|
||||
auto setPin = [&] (const QByteArray &path, PinState state) {
|
||||
@ -757,6 +759,7 @@ private slots:
|
||||
|
||||
setPin("local", PinState::AlwaysLocal);
|
||||
setPin("online", PinState::OnlineOnly);
|
||||
setPin("unspec", PinState::Unspecified);
|
||||
|
||||
// Test 1: root is OnlineOnly
|
||||
fakeFolder.remoteModifier().insert("file1");
|
||||
@ -782,7 +785,7 @@ private slots:
|
||||
QVERIFY(fakeFolder.currentLocalState().find("file2"));
|
||||
QVERIFY(fakeFolder.currentLocalState().find("online/file2.nextcloud"));
|
||||
QVERIFY(fakeFolder.currentLocalState().find("local/file2"));
|
||||
QVERIFY(fakeFolder.currentLocalState().find("unspec/file2"));
|
||||
QVERIFY(fakeFolder.currentLocalState().find("unspec/file2.nextcloud"));
|
||||
|
||||
// file1 is unchanged
|
||||
QVERIFY(fakeFolder.currentLocalState().find("file1.nextcloud"));
|
||||
@ -810,7 +813,7 @@ private slots:
|
||||
cleanup();
|
||||
|
||||
// Enable suffix vfs
|
||||
fakeFolder.syncEngine().setSyncOptions(vfsSyncOptions(fakeFolder));
|
||||
setupVfs(fakeFolder);
|
||||
|
||||
// Local changes of suffixed file do nothing
|
||||
fakeFolder.localModifier().appendByte("A/file1.nextcloud");
|
||||
|
Loading…
Reference in New Issue
Block a user