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
diff options
context:
space:
mode:
authorChristian Kamm <mail@ckamm.de>2019-04-03 11:53:04 +0300
committerChristian Kamm <mail@ckamm.de>2019-04-09 13:52:30 +0300
commit2b0f32c6459bfcde4c8e94fe086e861aee6c56e5 (patch)
tree4854b719e26528c14b182d968d606ea112e1c775
parent04268becd16a93e7c346d2b8789d3ee5d504d8ee (diff)
Vfs: Add 'availability', a simplified, user-facing pin state #7111
The idea is that the user's question is "is this folder's data available offline?" and not "does this folder have AlwaysLocal pin state?". The the answers to the two questions can differ: an always-local folder can have subitems that are not always-local and are dehydrated. The new availability enum intends to describe the answer to the user's actual question and can be derived from pin states. If pin states aren't stored in the database the way of calculating availability will depend on the vfs plugin.
-rw-r--r--src/common/pinstate.h41
-rw-r--r--src/common/syncjournaldb.cpp64
-rw-r--r--src/common/syncjournaldb.h21
-rw-r--r--src/common/vfs.cpp40
-rw-r--r--src/common/vfs.h21
-rw-r--r--src/gui/accountsettings.cpp16
-rw-r--r--src/gui/socketapi.cpp105
-rw-r--r--src/libsync/vfs/suffix/vfs_suffix.cpp9
-rw-r--r--src/libsync/vfs/suffix/vfs_suffix.h1
-rw-r--r--test/testsyncjournaldb.cpp23
-rw-r--r--test/testsyncvirtualfiles.cpp60
11 files changed, 336 insertions, 65 deletions
diff --git a/src/common/pinstate.h b/src/common/pinstate.h
index 053e7062f..44ee12417 100644
--- a/src/common/pinstate.h
+++ b/src/common/pinstate.h
@@ -73,6 +73,47 @@ enum class PinState {
Unspecified = 3,
};
+/** A user-facing version of PinState.
+ *
+ * PinStates communicate availability intent for an item, but particular
+ * situations can get complex: An AlwaysLocal folder can have OnlineOnly
+ * files or directories.
+ *
+ * For users this is condensed to a few useful cases.
+ *
+ * Note that this is only about *intent*. The file could still be out of date,
+ * or not have been synced for other reasons, like errors.
+ */
+enum class VfsItemAvailability {
+ /** The item and all its subitems are hydrated and pinned AlwaysLocal.
+ *
+ * This guarantees that all contents will be kept in sync.
+ */
+ AlwaysLocal,
+
+ /** The item and all its subitems are hydrated.
+ *
+ * This may change if the platform or client decide to dehydrate items
+ * that have Unspecified pin state.
+ *
+ * A folder with no file contents will have this availability.
+ */
+ AllHydrated,
+
+ /** There are dehydrated items but the pin state isn't all OnlineOnly.
+ *
+ * This would happen if a dehydration happens to a Unspecified item that
+ * used to be hydrated.
+ */
+ SomeDehydrated,
+
+ /** The item and all its subitems are dehydrated and OnlineOnly.
+ *
+ * This guarantees that contents will not take up space.
+ */
+ OnlineOnly,
+};
+
}
#endif
diff --git a/src/common/syncjournaldb.cpp b/src/common/syncjournaldb.cpp
index a97908a1f..89f6706f0 100644
--- a/src/common/syncjournaldb.cpp
+++ b/src/common/syncjournaldb.cpp
@@ -1253,6 +1253,31 @@ bool SyncJournalDb::updateLocalMetadata(const QString &filename,
return _setFileRecordLocalMetadataQuery.exec();
}
+Optional<bool> SyncJournalDb::hasDehydratedFiles(const QByteArray &filename)
+{
+ QMutexLocker locker(&_mutex);
+ if (!checkConnect())
+ return {};
+
+ auto &query = _countDehydratedFilesQuery;
+ static_assert(ItemTypeVirtualFile == 4 && ItemTypeVirtualFileDownload == 5, "");
+ if (!query.initOrReset(QByteArrayLiteral(
+ "SELECT count(*) FROM metadata"
+ " WHERE (" IS_PREFIX_PATH_OR_EQUAL("?1", "path") " OR ?1 == '')"
+ " AND (type == 4 OR type == 5);"), _db)) {
+ return {};
+ }
+
+ query.bindValue(1, filename);
+ if (!query.exec())
+ return {};
+
+ if (!query.next().hasData)
+ return {};
+
+ return query.intValue(0) > 0;
+}
+
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo *res)
{
bool ok = true;
@@ -2084,7 +2109,7 @@ Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPath(const QByt
// (it'd be great if paths started with a / and "/" could be the root)
" (" IS_PREFIX_PATH_OR_EQUAL("path", "?1") " OR path == '')"
" AND pinState is not null AND pinState != 0"
- " ORDER BY length(path) DESC;"),
+ " ORDER BY length(path) DESC LIMIT 1;"),
_db->_db));
query.bindValue(1, path);
query.exec();
@@ -2099,6 +2124,43 @@ Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPath(const QByt
return static_cast<PinState>(query.intValue(0));
}
+Optional<PinState> SyncJournalDb::PinStateInterface::effectiveForPathRecursive(const QByteArray &path)
+{
+ // Get the item's effective pin state. We'll compare subitem's pin states
+ // against this.
+ const auto basePin = effectiveForPath(path);
+ if (!basePin)
+ return {};
+
+ QMutexLocker lock(&_db->_mutex);
+ if (!_db->checkConnect())
+ return {};
+
+ // Find all the non-inherited pin states below the item
+ auto &query = _db->_getSubPinsQuery;
+ ASSERT(query.initOrReset(QByteArrayLiteral(
+ "SELECT DISTINCT pinState FROM flags WHERE"
+ " (" IS_PREFIX_PATH_OF("?1", "path") " OR ?1 == '')"
+ " AND pinState is not null and pinState != 0;"),
+ _db->_db));
+ query.bindValue(1, path);
+ query.exec();
+
+ // Check if they are all identical
+ forever {
+ auto next = query.next();
+ if (!next.ok)
+ return {};
+ if (!next.hasData)
+ break;
+ const auto subPin = static_cast<PinState>(query.intValue(0));
+ if (subPin != *basePin)
+ return PinState::Inherited;
+ }
+
+ return *basePin;
+}
+
void SyncJournalDb::PinStateInterface::setForPath(const QByteArray &path, PinState state)
{
QMutexLocker lock(&_db->_mutex);
diff --git a/src/common/syncjournaldb.h b/src/common/syncjournaldb.h
index 188f3028d..5aab475f1 100644
--- a/src/common/syncjournaldb.h
+++ b/src/common/syncjournaldb.h
@@ -71,6 +71,10 @@ public:
const QByteArray &contentChecksumType);
bool updateLocalMetadata(const QString &filename,
qint64 modtime, qint64 size, quint64 inode);
+
+ /** Returns whether the item or any subitems are dehydrated */
+ Optional<bool> hasDehydratedFiles(const QByteArray &filename);
+
bool exists();
void walCheckpoint();
@@ -296,6 +300,21 @@ public:
Optional<PinState> effectiveForPath(const QByteArray &path);
/**
+ * Like effectiveForPath() but also considers subitem pin states.
+ *
+ * If the path's pin state and all subitem's pin states are identical
+ * then that pin state will be returned.
+ *
+ * If some subitem's pin state is different from the path's state,
+ * PinState::Inherited will be returned. Inherited isn't returned in
+ * any other cases.
+ *
+ * It's valid to use the root path "".
+ * Returns none on db error.
+ */
+ Optional<PinState> effectiveForPathRecursive(const QByteArray &path);
+
+ /**
* Sets a path's pin state.
*
* The path should not have a trailing slash.
@@ -396,6 +415,8 @@ private:
SqlQuery _deleteConflictRecordQuery;
SqlQuery _getRawPinStateQuery;
SqlQuery _getEffectivePinStateQuery;
+ SqlQuery _getSubPinsQuery;
+ SqlQuery _countDehydratedFilesQuery;
SqlQuery _setPinStateQuery;
SqlQuery _wipePinStateQuery;
diff --git a/src/common/vfs.cpp b/src/common/vfs.cpp
index a40b396a7..5f170daf1 100644
--- a/src/common/vfs.cpp
+++ b/src/common/vfs.cpp
@@ -79,6 +79,27 @@ Optional<PinState> Vfs::pinStateInDb(const QString &folderPath)
return _setupParams.journal->internalPinStates().effectiveForPath(folderPath.toUtf8());
}
+Optional<VfsItemAvailability> Vfs::availabilityInDb(const QString &folderPath, const QString &pinPath)
+{
+ auto pin = _setupParams.journal->internalPinStates().effectiveForPathRecursive(pinPath.toUtf8());
+ // not being able to retrieve the pin state isn't too bad
+ Optional<bool> hasDehydrated = _setupParams.journal->hasDehydratedFiles(folderPath.toUtf8());
+ if (!hasDehydrated)
+ return {};
+
+ if (*hasDehydrated) {
+ if (pin && *pin == PinState::OnlineOnly)
+ return VfsItemAvailability::OnlineOnly;
+ else
+ return VfsItemAvailability::SomeDehydrated;
+ } else {
+ if (pin && *pin == PinState::AlwaysLocal)
+ return VfsItemAvailability::AlwaysLocal;
+ else
+ return VfsItemAvailability::AllHydrated;
+ }
+}
+
VfsOff::VfsOff(QObject *parent)
: Vfs(parent)
{
@@ -184,3 +205,22 @@ std::unique_ptr<Vfs> OCC::createVfsFromPlugin(Vfs::Mode mode)
qCInfo(lcPlugin) << "Created VFS instance from plugin" << pluginPath;
return vfs;
}
+
+QString OCC::vfsItemAvailabilityToString(VfsItemAvailability availability, bool forFolder)
+{
+ switch(availability) {
+ case VfsItemAvailability::AlwaysLocal:
+ return Vfs::tr("Always available locally");
+ case VfsItemAvailability::AllHydrated:
+ return Vfs::tr("Available locally");
+ case VfsItemAvailability::SomeDehydrated:
+ if (forFolder) {
+ return Vfs::tr("Some available online only");
+ } else {
+ return Vfs::tr("Available online only");
+ }
+ case VfsItemAvailability::OnlineOnly:
+ return Vfs::tr("Available online only");
+ }
+ ENFORCE(false);
+}
diff --git a/src/common/vfs.h b/src/common/vfs.h
index 2ccd3958d..a8765d793 100644
--- a/src/common/vfs.h
+++ b/src/common/vfs.h
@@ -189,10 +189,12 @@ public:
/** Sets the pin state for the item at a path.
*
+ * The pin state is set on the item and for all items below it.
+ *
* 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.
+ * folderPath is relative to the sync folder. Can be "" for root folder.
*/
virtual bool setPinState(const QString &folderPath, PinState state) = 0;
@@ -201,10 +203,19 @@ public:
* 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.
+ * folderPath is relative to the sync folder. Can be "" for root folder.
*/
virtual Optional<PinState> pinState(const QString &folderPath) = 0;
+ /** Returns availability status of an item at a path.
+ *
+ * The availability is a condensed user-facing version of PinState. See
+ * VfsItemAvailability for details.
+ *
+ * folderPath is relative to the sync folder. Can be "" for root folder.
+ */
+ virtual Optional<VfsItemAvailability> availability(const QString &folderPath) = 0;
+
public slots:
/** Update in-sync state based on SyncFileStatusTracker signal.
*
@@ -235,6 +246,8 @@ protected:
// Db-backed pin state handling. Derived classes may use it to implement pin states.
bool setPinStateInDb(const QString &folderPath, PinState state);
Optional<PinState> pinStateInDb(const QString &folderPath);
+ // sadly for virtual files the path in the metadata table can differ from path in 'flags'
+ Optional<VfsItemAvailability> availabilityInDb(const QString &folderPath, const QString &pinPath);
// the parameters passed to start()
VfsSetupParams _setupParams;
@@ -269,6 +282,7 @@ public:
bool setPinState(const QString &, PinState) override { return true; }
Optional<PinState> pinState(const QString &) override { return PinState::AlwaysLocal; }
+ Optional<VfsItemAvailability> availability(const QString &) override { return VfsItemAvailability::AlwaysLocal; }
public slots:
void fileStatusChanged(const QString &, SyncFileStatus) override {}
@@ -286,4 +300,7 @@ OCSYNC_EXPORT Vfs::Mode bestAvailableVfsMode();
/// Create a VFS instance for the mode, returns nullptr on failure.
OCSYNC_EXPORT std::unique_ptr<Vfs> createVfsFromPlugin(Vfs::Mode mode);
+/// Convert availability to translated string
+OCSYNC_EXPORT QString vfsItemAvailabilityToString(VfsItemAvailability availability, bool forFolder);
+
} // namespace OCC
diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp
index 0188332d3..b848330a2 100644
--- a/src/gui/accountsettings.cpp
+++ b/src/gui/accountsettings.cpp
@@ -340,14 +340,18 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
if (folder->supportsVirtualFiles()) {
auto availabilityMenu = menu->addMenu(tr("Availability"));
- ac = availabilityMenu->addAction(tr("Local"));
- ac->setCheckable(true);
- ac->setChecked(!folder->newFilesAreVirtual());
+ auto availability = folder->vfs().availability(QString());
+ if (availability) {
+ ac = availabilityMenu->addAction(vfsItemAvailabilityToString(*availability, true));
+ ac->setEnabled(false);
+ }
+
+ ac = availabilityMenu->addAction(tr("Make always available locally"));
+ ac->setEnabled(!availability || *availability != VfsItemAvailability::AlwaysLocal);
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::AlwaysLocal); });
- ac = availabilityMenu->addAction(tr("Online only"));
- ac->setCheckable(true);
- ac->setChecked(folder->newFilesAreVirtual());
+ ac = availabilityMenu->addAction(tr("Free up local space"));
+ ac->setEnabled(!availability || *availability != VfsItemAvailability::OnlineOnly);
connect(ac, &QAction::triggered, this, [this]() { slotSetCurrentFolderAvailability(PinState::OnlineOnly); });
ac = menu->addAction(tr("Disable virtual file support..."));
diff --git a/src/gui/socketapi.cpp b/src/gui/socketapi.cpp
index 092a84d20..c727b869a 100644
--- a/src/gui/socketapi.cpp
+++ b/src/gui/socketapi.cpp
@@ -946,68 +946,61 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
if (folder
&& folder->supportsVirtualFiles()
&& folder->vfs().socketApiPinStateActionsShown()) {
- bool hasAlwaysLocal = false;
- bool hasOnlineOnly = false;
- bool hasHydratedOnlineOnly = false;
- bool hasDehydratedOnlineOnly = false;
+ ENFORCE(!files.isEmpty());
+
+ // Determine the combined availability status of the files
+ auto combined = Optional<VfsItemAvailability>();
+ auto merge = [](VfsItemAvailability lhs, VfsItemAvailability rhs) {
+ if (lhs == rhs)
+ return lhs;
+ if (lhs == VfsItemAvailability::SomeDehydrated || rhs == VfsItemAvailability::SomeDehydrated
+ || lhs == VfsItemAvailability::OnlineOnly || rhs == VfsItemAvailability::OnlineOnly) {
+ return VfsItemAvailability::SomeDehydrated;
+ }
+ return VfsItemAvailability::AllHydrated;
+ };
+ bool isFolderOrMultiple = false;
for (const auto &file : files) {
auto fileData = FileData::get(file);
- auto path = fileData.folderRelativePathNoVfsSuffix();
- auto pinState = folder->vfs().pinState(path);
- if (!pinState) {
- // db error
- hasAlwaysLocal = true;
- hasOnlineOnly = true;
- } else if (*pinState == PinState::AlwaysLocal) {
- hasAlwaysLocal = true;
- } else if (*pinState == PinState::OnlineOnly) {
- hasOnlineOnly = true;
- auto record = fileData.journalRecord();
- if (record._type == ItemTypeFile)
- hasHydratedOnlineOnly = true;
- if (record.isVirtualFile())
- hasDehydratedOnlineOnly = true;
+ isFolderOrMultiple = QFileInfo(fileData.localPath).isDir();
+ auto availability = folder->vfs().availability(fileData.folderRelativePath);
+ if (!availability)
+ availability = VfsItemAvailability::SomeDehydrated; // db error
+ if (!combined) {
+ combined = availability;
+ } else {
+ combined = merge(*combined, *availability);
}
}
-
- auto makePinContextMenu = [listener](QString currentState, QString availableLocally, QString onlineOnly) {
- listener->sendMessage(QLatin1String("MENU_ITEM:CURRENT_PIN:d:") + currentState);
- if (!availableLocally.isEmpty())
- listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY::") + availableLocally);
- if (!onlineOnly.isEmpty())
- listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_ONLINE_ONLY::") + onlineOnly);
+ ENFORCE(combined);
+ if (files.size() > 1)
+ isFolderOrMultiple = true;
+
+ // TODO: Should be a submenu, should use icons
+ auto makePinContextMenu = [&](bool makeAvailableLocally, bool freeSpace) {
+ listener->sendMessage(QLatin1String("MENU_ITEM:CURRENT_PIN:d:")
+ + vfsItemAvailabilityToString(*combined, isFolderOrMultiple));
+ listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_AVAILABLE_LOCALLY:")
+ + (makeAvailableLocally ? QLatin1String(":") : QLatin1String("d:"))
+ + tr("Make always available locally"));
+ listener->sendMessage(QLatin1String("MENU_ITEM:MAKE_ONLINE_ONLY:")
+ + (freeSpace ? QLatin1String(":") : QLatin1String("d:"))
+ + tr("Free up local space"));
};
- // TODO: Should be a submenu, should use menu item checkmarks where available, should use icons
- if (hasAlwaysLocal) {
- if (!hasOnlineOnly) {
- makePinContextMenu(
- tr("Currently available locally"),
- QString(),
- tr("Make available online only"));
- } else { // local + online
- makePinContextMenu(
- tr("Current availability is mixed"),
- tr("Make all available locally"),
- tr("Make all available online only"));
- }
- } else if (hasOnlineOnly) {
- if (hasDehydratedOnlineOnly && !hasHydratedOnlineOnly) {
- makePinContextMenu(
- tr("Currently available online only"),
- tr("Make available locally"),
- QString());
- } else if (hasHydratedOnlineOnly && !hasDehydratedOnlineOnly) {
- makePinContextMenu(
- tr("Currently available, but marked online only"),
- tr("Make available locally"),
- tr("Make available online only"));
- } else { // hydrated + dehydrated
- makePinContextMenu(
- tr("Some currently available, all marked online only"),
- tr("Make available locally"),
- tr("Make available online only"));
- }
+ switch (*combined) {
+ case VfsItemAvailability::AlwaysLocal:
+ makePinContextMenu(false, true);
+ break;
+ case VfsItemAvailability::AllHydrated:
+ makePinContextMenu(true, true);
+ break;
+ case VfsItemAvailability::SomeDehydrated:
+ makePinContextMenu(true, true);
+ break;
+ case VfsItemAvailability::OnlineOnly:
+ makePinContextMenu(true, false);
+ break;
}
}
diff --git a/src/libsync/vfs/suffix/vfs_suffix.cpp b/src/libsync/vfs/suffix/vfs_suffix.cpp
index 9860c24b2..0480d02b2 100644
--- a/src/libsync/vfs/suffix/vfs_suffix.cpp
+++ b/src/libsync/vfs/suffix/vfs_suffix.cpp
@@ -105,4 +105,13 @@ bool VfsSuffix::statTypeVirtualFile(csync_file_stat_t *stat, void *)
return false;
}
+Optional<VfsItemAvailability> VfsSuffix::availability(const QString &folderPath)
+{
+ const auto suffix = fileSuffix();
+ QString pinPath = folderPath;
+ if (pinPath.endsWith(suffix))
+ pinPath.chop(suffix.size());
+ return availabilityInDb(folderPath, pinPath);
+}
+
} // namespace OCC
diff --git a/src/libsync/vfs/suffix/vfs_suffix.h b/src/libsync/vfs/suffix/vfs_suffix.h
index 6c42bcfb4..5aadf5449 100644
--- a/src/libsync/vfs/suffix/vfs_suffix.h
+++ b/src/libsync/vfs/suffix/vfs_suffix.h
@@ -51,6 +51,7 @@ public:
{ return setPinStateInDb(folderPath, state); }
Optional<PinState> pinState(const QString &folderPath) override
{ return pinStateInDb(folderPath); }
+ Optional<VfsItemAvailability> availability(const QString &folderPath) override;
public slots:
void fileStatusChanged(const QString &, SyncFileStatus) override {}
diff --git a/test/testsyncjournaldb.cpp b/test/testsyncjournaldb.cpp
index ceb23d4cc..09e4df048 100644
--- a/test/testsyncjournaldb.cpp
+++ b/test/testsyncjournaldb.cpp
@@ -343,6 +343,14 @@ private slots:
}
return *state;
};
+ auto getRecursive = [&](const QByteArray &path) -> PinState {
+ auto state = _db.internalPinStates().effectiveForPathRecursive(path);
+ if (!state) {
+ QTest::qFail("couldn't read pin state", __FILE__, __LINE__);
+ return PinState::Inherited;
+ }
+ return *state;
+ };
auto getRaw = [&](const QByteArray &path) -> PinState {
auto state = _db.internalPinStates().rawForPath(path);
if (!state) {
@@ -377,6 +385,7 @@ private slots:
QCOMPARE(list->size(), 4 + 9 + 27);
// Baseline direct checks (the fallback for unset root pinstate is AlwaysLocal)
+ QCOMPARE(get(""), PinState::AlwaysLocal);
QCOMPARE(get("local"), PinState::AlwaysLocal);
QCOMPARE(get("online"), PinState::OnlineOnly);
QCOMPARE(get("inherit"), PinState::AlwaysLocal);
@@ -406,6 +415,20 @@ private slots:
QCOMPARE(get("online/online/inherit"), PinState::OnlineOnly);
QCOMPARE(get("online/online/nonexistant"), PinState::OnlineOnly);
+ // Spot check the recursive variant
+ QCOMPARE(getRecursive(""), PinState::Inherited);
+ QCOMPARE(getRecursive("local"), PinState::Inherited);
+ QCOMPARE(getRecursive("online"), PinState::Inherited);
+ QCOMPARE(getRecursive("inherit"), PinState::Inherited);
+ QCOMPARE(getRecursive("online/local"), PinState::Inherited);
+ QCOMPARE(getRecursive("online/local/inherit"), PinState::AlwaysLocal);
+ QCOMPARE(getRecursive("inherit/inherit/inherit"), PinState::AlwaysLocal);
+ QCOMPARE(getRecursive("inherit/online/inherit"), PinState::OnlineOnly);
+ QCOMPARE(getRecursive("inherit/online/local"), PinState::AlwaysLocal);
+ make("local/local/local/local", PinState::AlwaysLocal);
+ QCOMPARE(getRecursive("local/local/local"), PinState::AlwaysLocal);
+ QCOMPARE(getRecursive("local/local/local/local"), PinState::AlwaysLocal);
+
// Check changing the root pin state
make("", PinState::OnlineOnly);
QCOMPARE(get("local"), PinState::AlwaysLocal);
diff --git a/test/testsyncvirtualfiles.cpp b/test/testsyncvirtualfiles.cpp
index dbb0beb27..ae2bb3003 100644
--- a/test/testsyncvirtualfiles.cpp
+++ b/test/testsyncvirtualfiles.cpp
@@ -1024,6 +1024,66 @@ private slots:
QVERIFY(!fakeFolder.currentLocalState().find("A/file2.owncloud.owncloud"));
cleanup();
}
+
+ void testAvailability()
+ {
+ FakeFolder fakeFolder{ FileInfo() };
+ auto vfs = setupVfs(fakeFolder);
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ auto setPin = [&] (const QByteArray &path, PinState state) {
+ fakeFolder.syncJournal().internalPinStates().setForPath(path, state);
+ };
+
+ fakeFolder.remoteModifier().mkdir("local");
+ fakeFolder.remoteModifier().mkdir("local/sub");
+ fakeFolder.remoteModifier().mkdir("online");
+ fakeFolder.remoteModifier().mkdir("online/sub");
+ fakeFolder.remoteModifier().mkdir("unspec");
+ QVERIFY(fakeFolder.syncOnce());
+ QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
+
+ setPin("local", PinState::AlwaysLocal);
+ setPin("online", PinState::OnlineOnly);
+ setPin("unspec", PinState::Unspecified);
+
+ fakeFolder.remoteModifier().insert("file1");
+ fakeFolder.remoteModifier().insert("online/file1");
+ fakeFolder.remoteModifier().insert("online/file2");
+ fakeFolder.remoteModifier().insert("local/file1");
+ fakeFolder.remoteModifier().insert("local/file2");
+ fakeFolder.remoteModifier().insert("unspec/file1");
+ QVERIFY(fakeFolder.syncOnce());
+
+ // root is unspecified
+ QCOMPARE(*vfs->availability("file1"), VfsItemAvailability::AllHydrated);
+ QCOMPARE(*vfs->availability("local"), VfsItemAvailability::AlwaysLocal);
+ QCOMPARE(*vfs->availability("local/file1"), VfsItemAvailability::AlwaysLocal);
+ QCOMPARE(*vfs->availability("online"), VfsItemAvailability::OnlineOnly);
+ QCOMPARE(*vfs->availability("online/file1.owncloud"), VfsItemAvailability::OnlineOnly);
+ QCOMPARE(*vfs->availability("unspec"), VfsItemAvailability::SomeDehydrated);
+ QCOMPARE(*vfs->availability("unspec/file1.owncloud"), VfsItemAvailability::SomeDehydrated);
+
+ // Subitem pin states can ruin "pure" availabilities
+ setPin("local/sub", PinState::OnlineOnly);
+ QCOMPARE(*vfs->availability("local"), VfsItemAvailability::AllHydrated);
+ setPin("online/sub", PinState::Unspecified);
+ QCOMPARE(*vfs->availability("online"), VfsItemAvailability::SomeDehydrated);
+
+ triggerDownload(fakeFolder, "unspec/file1");
+ setPin("local/file2", PinState::OnlineOnly);
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(*vfs->availability("unspec"), VfsItemAvailability::AllHydrated);
+ QCOMPARE(*vfs->availability("local"), VfsItemAvailability::SomeDehydrated);
+
+ vfs->setPinState("local", PinState::AlwaysLocal);
+ vfs->setPinState("online", PinState::OnlineOnly);
+ QVERIFY(fakeFolder.syncOnce());
+
+ QCOMPARE(*vfs->availability("online"), VfsItemAvailability::OnlineOnly);
+ QCOMPARE(*vfs->availability("local"), VfsItemAvailability::AlwaysLocal);
+ }
};
QTEST_GUILESS_MAIN(TestSyncVirtualFiles)