chore(Storybook): Exclude DirectoryFilesWatcher from PagesModel, add tests
This commit is contained in:
parent
a567910f3b
commit
5556c2a52f
|
@ -45,6 +45,7 @@ set(PROJECT_LIB "${PROJECT_NAME}Lib")
|
||||||
add_library(${PROJECT_LIB}
|
add_library(${PROJECT_LIB}
|
||||||
cachecleaner.cpp cachecleaner.h
|
cachecleaner.cpp cachecleaner.h
|
||||||
directorieswatcher.cpp directorieswatcher.h
|
directorieswatcher.cpp directorieswatcher.h
|
||||||
|
directoryfileswatcher.cpp directoryfileswatcher.h
|
||||||
figmaio.cpp figmaio.h
|
figmaio.cpp figmaio.h
|
||||||
figmalinks.cpp figmalinks.h
|
figmalinks.cpp figmalinks.h
|
||||||
figmalinksmodel.cpp figmalinksmodel.h
|
figmalinksmodel.cpp figmalinksmodel.h
|
||||||
|
@ -96,6 +97,10 @@ add_executable(SectionsDecoratorModelTest tests/tst_SectionsDecoratorModel.cpp)
|
||||||
target_link_libraries(SectionsDecoratorModelTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
target_link_libraries(SectionsDecoratorModelTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
||||||
add_test(NAME SectionsDecoratorModelTest COMMAND SectionsDecoratorModelTest)
|
add_test(NAME SectionsDecoratorModelTest COMMAND SectionsDecoratorModelTest)
|
||||||
|
|
||||||
|
add_executable(DirectoryFilesWatcherTest tests/tst_DirectoryFilesWatcher.cpp)
|
||||||
|
target_link_libraries(DirectoryFilesWatcherTest PRIVATE Qt5::Test ${PROJECT_LIB})
|
||||||
|
add_test(NAME DirectoryFilesWatcherTest COMMAND DirectoryFilesWatcherTest)
|
||||||
|
|
||||||
add_executable(QmlTests
|
add_executable(QmlTests
|
||||||
qmlTests/main.cpp
|
qmlTests/main.cpp
|
||||||
qmlTests/src/TextUtils.cpp qmlTests/src/TextUtils.h
|
qmlTests/src/TextUtils.cpp qmlTests/src/TextUtils.h
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
#include "directoryfileswatcher.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFileSystemWatcher>
|
||||||
|
|
||||||
|
DirectoryFilesWatcher::DirectoryFilesWatcher(
|
||||||
|
const QString &path, const QString &pattern, QObject *parent)
|
||||||
|
: QObject{parent}, m_path{path}, m_pattern{pattern},
|
||||||
|
m_fsWatcher{new QFileSystemWatcher(this)}
|
||||||
|
{
|
||||||
|
auto files = readDirectory();
|
||||||
|
m_files.reserve(files.size());
|
||||||
|
m_files.insert(std::move_iterator(files.begin()),
|
||||||
|
std::move_iterator(files.end()));
|
||||||
|
|
||||||
|
m_fsWatcher->addPath(path);
|
||||||
|
|
||||||
|
connect(m_fsWatcher, &QFileSystemWatcher::directoryChanged,
|
||||||
|
this, &DirectoryFilesWatcher::onDirectoryChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList DirectoryFilesWatcher::files() const
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
list.reserve(m_files.size());
|
||||||
|
|
||||||
|
for (auto& [file, _] : m_files)
|
||||||
|
list << file;
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<QString, QDateTime>> DirectoryFilesWatcher::readDirectory() const
|
||||||
|
{
|
||||||
|
QDir dir(m_path);
|
||||||
|
dir.setFilter(QDir::Files);
|
||||||
|
dir.setNameFilters({m_pattern});
|
||||||
|
|
||||||
|
const QFileInfoList filesInfo = dir.entryInfoList();
|
||||||
|
std::vector<std::pair<QString, QDateTime>> files;
|
||||||
|
files.reserve(filesInfo.size());
|
||||||
|
|
||||||
|
std::transform(filesInfo.begin(), filesInfo.end(),
|
||||||
|
std::back_inserter(files),
|
||||||
|
[] (auto &info) {
|
||||||
|
return std::make_pair(info.filePath(), info.lastModified());
|
||||||
|
});
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DirectoryFilesWatcher::onDirectoryChanged() {
|
||||||
|
auto files = readDirectory();
|
||||||
|
|
||||||
|
QStringList added;
|
||||||
|
QStringList removed;
|
||||||
|
QStringList changed;
|
||||||
|
|
||||||
|
for (auto& [file, date] : files) {
|
||||||
|
auto it = m_files.find(file);
|
||||||
|
|
||||||
|
if (it == m_files.end()) {
|
||||||
|
added.push_back(file);
|
||||||
|
} else {
|
||||||
|
if (date != it->second)
|
||||||
|
changed.push_back(file);
|
||||||
|
|
||||||
|
m_files.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [file, date] : m_files)
|
||||||
|
removed.push_back(file);
|
||||||
|
|
||||||
|
m_files.clear();
|
||||||
|
m_files.reserve(files.size());
|
||||||
|
m_files.insert(std::move_iterator(files.begin()),
|
||||||
|
std::move_iterator(files.end()));
|
||||||
|
|
||||||
|
emit filesChanged(added, removed, changed);
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
class QFileSystemWatcher;
|
||||||
|
|
||||||
|
class DirectoryFilesWatcher : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DirectoryFilesWatcher(const QString &path, const QString &pattern,
|
||||||
|
QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QStringList files() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void filesChanged(const QStringList& added, const QStringList& removed,
|
||||||
|
const QStringList& changed);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<QString, QDateTime>> readDirectory() const;
|
||||||
|
void onDirectoryChanged();
|
||||||
|
|
||||||
|
QString m_path;
|
||||||
|
QString m_pattern;
|
||||||
|
std::unordered_map<QString, QDateTime> m_files;
|
||||||
|
QFileSystemWatcher* m_fsWatcher;
|
||||||
|
};
|
|
@ -1,10 +1,10 @@
|
||||||
#include "pagesmodel.h"
|
#include "pagesmodel.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QFileInfo>
|
||||||
#include <QFileSystemWatcher>
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include "directoryfileswatcher.h"
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const auto categoryUncategorized QStringLiteral("Uncategorized");
|
const auto categoryUncategorized QStringLiteral("Uncategorized");
|
||||||
|
@ -12,62 +12,37 @@ const auto categoryUncategorized QStringLiteral("Uncategorized");
|
||||||
|
|
||||||
PagesModel::PagesModel(const QString &path, QObject *parent)
|
PagesModel::PagesModel(const QString &path, QObject *parent)
|
||||||
: QAbstractListModel{parent}, m_path{path},
|
: QAbstractListModel{parent}, m_path{path},
|
||||||
fsWatcher(new QFileSystemWatcher(this))
|
m_pagesWatcher(new DirectoryFilesWatcher(
|
||||||
|
path, QStringLiteral("*Page.qml"), this))
|
||||||
{
|
{
|
||||||
m_items = load();
|
m_items = readMetadata(m_pagesWatcher->files());
|
||||||
readMetadata(m_items);
|
|
||||||
|
|
||||||
for (const auto& item : qAsConst(m_items)) {
|
for (const auto& item : qAsConst(m_items))
|
||||||
setFigmaLinks(item.title, item.figmaLinks);
|
setFigmaLinks(item.title, item.figmaLinks);
|
||||||
|
|
||||||
|
connect(m_pagesWatcher, &DirectoryFilesWatcher::filesChanged,
|
||||||
|
this, &PagesModel::onPagesChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
fsWatcher->addPath(path);
|
PagesModelItem PagesModel::readMetadata(const QString& path)
|
||||||
|
{
|
||||||
connect(fsWatcher, &QFileSystemWatcher::directoryChanged,
|
|
||||||
this, &PagesModel::reload);
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<PagesModelItem> PagesModel::load() const {
|
|
||||||
static QRegularExpression fileNameRegex(
|
|
||||||
QRegularExpression::anchoredPattern("(.*)Page\\.qml"));
|
|
||||||
|
|
||||||
QDir dir(m_path);
|
|
||||||
dir.setFilter(QDir::Files);
|
|
||||||
|
|
||||||
const QFileInfoList files = dir.entryInfoList();
|
|
||||||
QList<PagesModelItem> items;
|
|
||||||
|
|
||||||
std::for_each(files.begin(), files.end(), [this, &items] (auto &fileInfo) {
|
|
||||||
QString fileName = fileInfo.fileName();
|
|
||||||
QRegularExpressionMatch fileNameMatch = fileNameRegex.match(fileName);
|
|
||||||
|
|
||||||
if (!fileNameMatch.hasMatch())
|
|
||||||
return;
|
|
||||||
|
|
||||||
PagesModelItem item;
|
PagesModelItem item;
|
||||||
item.path = fileInfo.filePath();
|
item.path = path;
|
||||||
item.title = fileNameMatch.captured(1);
|
item.title = QFileInfo(path).fileName().chopped(
|
||||||
item.lastModified = fileInfo.lastModified();
|
(QStringLiteral("Page.qml").size()));
|
||||||
|
|
||||||
items << item;
|
QFile file(path);
|
||||||
});
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PagesModel::readMetadata(PagesModelItem& item) {
|
|
||||||
static QRegularExpression categoryRegex(
|
|
||||||
"^//(\\s)*category:(.+)$", QRegularExpression::MultilineOption);
|
|
||||||
|
|
||||||
QFile file(item.path);
|
|
||||||
file.open(QIODevice::ReadOnly);
|
file.open(QIODevice::ReadOnly);
|
||||||
QByteArray content = file.readAll();
|
QByteArray content = file.readAll();
|
||||||
|
|
||||||
|
static QRegularExpression categoryRegex(
|
||||||
|
"^//(\\s)*category:(.+)$", QRegularExpression::MultilineOption);
|
||||||
|
|
||||||
QRegularExpressionMatch categoryMatch = categoryRegex.match(content);
|
QRegularExpressionMatch categoryMatch = categoryRegex.match(content);
|
||||||
QString category = categoryMatch.hasMatch()
|
QString category = categoryMatch.hasMatch()
|
||||||
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
? categoryMatch.captured(2).trimmed() : categoryUncategorized;
|
||||||
|
|
||||||
item.category = category;
|
item.category = category.size() ? category : categoryUncategorized;
|
||||||
|
|
||||||
static QRegularExpression figmaRegex(
|
static QRegularExpression figmaRegex(
|
||||||
"^(\\/\\/\\s*)((?:https:\\/\\/)?(?:www\\.)?figma\\.com\\/.*)$",
|
"^(\\/\\/\\s*)((?:https:\\/\\/)?(?:www\\.)?figma\\.com\\/.*)$",
|
||||||
|
@ -83,90 +58,78 @@ void PagesModel::readMetadata(PagesModelItem& item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
item.figmaLinks = links;
|
item.figmaLinks = links;
|
||||||
|
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PagesModel::readMetadata(QList<PagesModelItem> &items) {
|
QList<PagesModelItem> PagesModel::readMetadata(const QStringList &paths)
|
||||||
std::for_each(items.begin(), items.end(), [](auto&item) {
|
{
|
||||||
readMetadata(item);
|
QList<PagesModelItem> metadata;
|
||||||
});
|
metadata.reserve(paths.size());
|
||||||
}
|
|
||||||
|
|
||||||
void PagesModel::reload() {
|
std::transform(paths.begin(), paths.end(), std::back_inserter(metadata),
|
||||||
const QList<PagesModelItem> currentItems = load();
|
[](auto& path) {
|
||||||
std::unordered_map<QString, PagesModelItem> mapping;
|
return readMetadata(path);
|
||||||
|
|
||||||
for (const PagesModelItem &item : qAsConst(m_items))
|
|
||||||
mapping[item.title] = item;
|
|
||||||
|
|
||||||
std::vector<PagesModelItem> newItems;
|
|
||||||
std::vector<PagesModelItem> changedItems;
|
|
||||||
std::vector<PagesModelItem> removedItems;
|
|
||||||
|
|
||||||
for (const auto &item : currentItems) {
|
|
||||||
auto it = mapping.find(item.title);
|
|
||||||
|
|
||||||
if (it == mapping.end()) {
|
|
||||||
newItems.push_back(item);
|
|
||||||
} else {
|
|
||||||
if (item.lastModified != it->second.lastModified)
|
|
||||||
changedItems.push_back(item);
|
|
||||||
|
|
||||||
mapping.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& [key, value] : mapping)
|
|
||||||
removedItems.push_back(value);
|
|
||||||
|
|
||||||
for (auto& item : removedItems) {
|
|
||||||
|
|
||||||
auto it = std::find_if(m_items.begin(), m_items.end(), [&item](auto& it){
|
|
||||||
return it.title == item.title;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
auto index = std::distance(m_items.begin(), it);
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
beginRemoveRows(QModelIndex{}, index, index);
|
void PagesModel::onPagesChanged(const QStringList& added,
|
||||||
|
const QStringList& removed,
|
||||||
|
const QStringList& changed)
|
||||||
|
{
|
||||||
|
for (auto& path : removed) {
|
||||||
|
auto index = getIndexByPath(path);
|
||||||
|
|
||||||
|
beginRemoveRows({}, index, index);
|
||||||
m_items.removeAt(index);
|
m_items.removeAt(index);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newItems.size()) {
|
if (added.size()) {
|
||||||
beginInsertRows(QModelIndex{}, rowCount(), rowCount() + newItems.size() - 1);
|
beginInsertRows({}, rowCount(), rowCount() + added.size() - 1);
|
||||||
|
|
||||||
for (auto& item : newItems) {
|
auto metadata = readMetadata(added);
|
||||||
readMetadata(item);
|
|
||||||
m_items << item;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (const auto& item : qAsConst(metadata))
|
||||||
|
setFigmaLinks(item.title, item.figmaLinks);
|
||||||
|
|
||||||
|
m_items << metadata;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& item : changedItems) {
|
for (auto& path : changed) {
|
||||||
auto it = std::find_if(m_items.begin(), m_items.end(), [&item](auto& it){
|
auto index = getIndexByPath(path);
|
||||||
return it.title == item.title;
|
const auto& previous = m_items.at(index);
|
||||||
});
|
|
||||||
|
|
||||||
auto index = std::distance(m_items.begin(), it);
|
PagesModelItem metadata = readMetadata(path);
|
||||||
const auto& previous = *it;
|
setFigmaLinks(metadata.title, metadata.figmaLinks);
|
||||||
readMetadata(item);
|
|
||||||
setFigmaLinks(item.title, item.figmaLinks);
|
|
||||||
|
|
||||||
if (previous.category != item.category) {
|
if (previous.category != metadata.category) {
|
||||||
// For simplicity category change is handled by removing and
|
// For simplicity category change is handled by removing and
|
||||||
// adding item. In the future it can be changed to regular dataChanged
|
// adding item. In the future it can be changed to regular dataChanged
|
||||||
// event and handled properly in upstream models like SectionSDecoratorModel.
|
// event and handled properly in upstream models like SectionSDecoratorModel.
|
||||||
beginRemoveRows(QModelIndex{}, index, index);
|
beginRemoveRows({}, index, index);
|
||||||
m_items.removeAt(index);
|
m_items.removeAt(index);
|
||||||
endRemoveRows();
|
endRemoveRows();
|
||||||
|
|
||||||
beginInsertRows(QModelIndex{}, rowCount(), rowCount());
|
beginInsertRows({}, rowCount(), rowCount());
|
||||||
m_items << item;
|
m_items << metadata;
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PagesModel::getIndexByPath(const QString& path) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(m_items.begin(), m_items.end(), [&path](auto& it) {
|
||||||
|
return it.path == path;
|
||||||
|
});
|
||||||
|
assert(it != m_items.end());
|
||||||
|
return std::distance(m_items.begin(), it);
|
||||||
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray> PagesModel::roleNames() const
|
QHash<int, QByteArray> PagesModel::roleNames() const
|
||||||
{
|
{
|
||||||
static const QHash<int,QByteArray> roles {
|
static const QHash<int,QByteArray> roles {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "figmalinksmodel.h"
|
#include "figmalinksmodel.h"
|
||||||
|
|
||||||
class QFileSystemWatcher;
|
class DirectoryFilesWatcher;
|
||||||
|
|
||||||
struct PagesModelItem {
|
struct PagesModelItem {
|
||||||
QString path;
|
QString path;
|
||||||
|
@ -31,9 +31,15 @@ public:
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
void reload();
|
|
||||||
private:
|
private:
|
||||||
QList<PagesModelItem> load() const;
|
void onPagesChanged(const QStringList& added, const QStringList& removed,
|
||||||
|
const QStringList& changed);
|
||||||
|
|
||||||
|
int getIndexByPath(const QString& path) const;
|
||||||
|
|
||||||
|
static PagesModelItem readMetadata(const QString& path);
|
||||||
|
static QList<PagesModelItem> readMetadata(const QStringList& paths);
|
||||||
|
|
||||||
static void readMetadata(PagesModelItem &item);
|
static void readMetadata(PagesModelItem &item);
|
||||||
static void readMetadata(QList<PagesModelItem> &items);
|
static void readMetadata(QList<PagesModelItem> &items);
|
||||||
|
@ -43,5 +49,5 @@ private:
|
||||||
QString m_path;
|
QString m_path;
|
||||||
QList<PagesModelItem> m_items;
|
QList<PagesModelItem> m_items;
|
||||||
QMap<QString, FigmaLinksModel*> m_figmaSubmodels;
|
QMap<QString, FigmaLinksModel*> m_figmaSubmodels;
|
||||||
QFileSystemWatcher* fsWatcher;
|
DirectoryFilesWatcher* m_pagesWatcher;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <directoryfileswatcher.h>
|
||||||
|
|
||||||
|
|
||||||
|
class TestDirectoryFilesWatcher: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void emptyDirTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
QVERIFY(watcher.files().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void nonEmptyDirTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
|
||||||
|
QString filename = dir.path() + "/Data.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
QCOMPARE(watcher.files().size(), 1);
|
||||||
|
QCOMPARE(watcher.files().at(0), filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyAddTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
|
||||||
|
QSignalSpy changeSpy(&watcher, &DirectoryFilesWatcher::filesChanged);
|
||||||
|
|
||||||
|
|
||||||
|
QString filename = dir.path() + "/Data.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSpy.wait();
|
||||||
|
QCOMPARE(changeSpy.count(), 1);
|
||||||
|
QCOMPARE(changeSpy.at(0).at(0).toStringList(), { filename });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(1).toStringList(), { });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(2).toStringList(), { });
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyMultipleAddTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
|
||||||
|
QSignalSpy changeSpy(&watcher, &DirectoryFilesWatcher::filesChanged);
|
||||||
|
|
||||||
|
|
||||||
|
QString filename = dir.path() + "/Data.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filename2 = dir.path() + "/Data2.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename2);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeSpy.wait();
|
||||||
|
QCOMPARE(changeSpy.count(), 1);
|
||||||
|
QCOMPARE(changeSpy.at(0).at(0).toStringList(),
|
||||||
|
QStringList({ filename, filename2 }));
|
||||||
|
QCOMPARE(changeSpy.at(0).at(1).toStringList(), { });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(2).toStringList(), { });
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyRemoveTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
QString filename = dir.path() + "/Data.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filename2 = dir.path() + "/Data2.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename2);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
|
||||||
|
QSignalSpy changeSpy(&watcher, &DirectoryFilesWatcher::filesChanged);
|
||||||
|
|
||||||
|
QVERIFY(QFile::remove(filename));
|
||||||
|
|
||||||
|
|
||||||
|
changeSpy.wait();
|
||||||
|
QCOMPARE(changeSpy.count(), 1);
|
||||||
|
QCOMPARE(changeSpy.at(0).at(0).toStringList(), { });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(1).toStringList(), { filename });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(2).toStringList(), { });
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyChangeTest()
|
||||||
|
{
|
||||||
|
QTemporaryDir dir;
|
||||||
|
QVERIFY(dir.isValid());
|
||||||
|
|
||||||
|
QString filename = dir.path() + "/Data.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString filename2 = dir.path() + "/Data2.txt";
|
||||||
|
{
|
||||||
|
QFile file(filename2);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryFilesWatcher watcher(dir.path(), "*");
|
||||||
|
|
||||||
|
QSignalSpy changeSpy(&watcher, &DirectoryFilesWatcher::filesChanged);
|
||||||
|
|
||||||
|
// wait a bit to have a different timestamp for tmp file
|
||||||
|
QTest::qSleep(5);
|
||||||
|
|
||||||
|
QString tempraryFilename = dir.path() + "/Tmp.txt";
|
||||||
|
{
|
||||||
|
QFile file(tempraryFilename);
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
QTextStream stream(&file);
|
||||||
|
stream << "something else";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile::remove(filename);
|
||||||
|
QFile::rename(tempraryFilename, filename);
|
||||||
|
|
||||||
|
changeSpy.wait();
|
||||||
|
QCOMPARE(changeSpy.count(), 1);
|
||||||
|
QCOMPARE(changeSpy.at(0).at(0).toStringList(), { });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(1).toStringList(), { });
|
||||||
|
QCOMPARE(changeSpy.at(0).at(2).toStringList(), { filename });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(TestDirectoryFilesWatcher)
|
||||||
|
#include "tst_DirectoryFilesWatcher.moc"
|
Loading…
Reference in New Issue