fs-watcher: Switch code to use ndn-cxx
Implementation moved to ndn::chronoshare namespace
Change-Id: I78a7f08dc2aafe7a2d578f78d99c72db7ea414b6
diff --git a/.jenkins.d/20-tests.sh b/.jenkins.d/20-tests.sh
index 6f466ed..db8cf30 100755
--- a/.jenkins.d/20-tests.sh
+++ b/.jenkins.d/20-tests.sh
@@ -47,6 +47,9 @@
# Give NFD a few seconds to start
sleep 10
+#open the NDN LOG
+export NDN_LOG=*=ALL
+
# First run all tests as unprivileged user
./build/unit-tests $(ut_log_args)
diff --git a/fs-watcher/fs-watcher.cpp b/fs-watcher/fs-watcher.cpp
index 65305a4..6e637cf 100644
--- a/fs-watcher/fs-watcher.cpp
+++ b/fs-watcher/fs-watcher.cpp
@@ -19,24 +19,23 @@
*/
#include "fs-watcher.hpp"
-#include "db-helper.hpp"
-#include "logging.hpp"
-
-#include <boost/bind.hpp>
+#include "core/logging.hpp"
#include <QDirIterator>
#include <QRegExp>
-using namespace std;
-using namespace boost;
+namespace ndn {
+namespace chronoshare {
_LOG_INIT(FsWatcher);
-FsWatcher::FsWatcher(QString dirPath, LocalFile_Change_Callback onChange,
+namespace fs = boost::filesystem;
+
+FsWatcher::FsWatcher(boost::asio::io_service& io, QString dirPath, LocalFile_Change_Callback onChange,
LocalFile_Change_Callback onDelete, QObject* parent)
: QObject(parent)
- , m_watcher(new QFileSystemWatcher())
- , m_scheduler(new Scheduler())
+ , m_watcher(new QFileSystemWatcher(this))
+ , m_scheduler(io)
, m_dirPath(dirPath)
, m_onChange(onChange)
, m_onDelete(onDelete)
@@ -48,61 +47,56 @@
m_watcher->addPath(m_dirPath);
- // register signals (callback functions)
+ // register signals(callback functions)
connect(m_watcher, SIGNAL(directoryChanged(QString)), this, SLOT(DidDirectoryChanged(QString)));
connect(m_watcher, SIGNAL(fileChanged(QString)), this, SLOT(DidFileChanged(QString)));
- m_scheduler->start();
+ rescheduleEvent("rescan", m_dirPath.toStdString(), time::seconds(0),
+ bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, m_dirPath));
- Scheduler::scheduleOneTimeTask(m_scheduler, 0,
- bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this,
- m_dirPath),
- "rescan-r-" +
- m_dirPath.toStdString()); // only one task will be scheduled per directory
-
- Scheduler::scheduleOneTimeTask(m_scheduler, 0, bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute,
- this, m_dirPath),
- "rescan-" +
- m_dirPath.toStdString()); // only one task will be scheduled per directory
+ rescheduleEvent("rescan-r", m_dirPath.toStdString(), time::seconds(0),
+ bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath));
}
FsWatcher::~FsWatcher()
{
- m_scheduler->shutdown();
sqlite3_close(m_db);
}
void
+FsWatcher::rescheduleEvent(const std::string& eventType, const std::string& path,
+ const time::milliseconds& period, const Scheduler::Event& callback)
+{
+ util::scheduler::ScopedEventId event(m_scheduler);
+ event = m_scheduler.scheduleEvent(period, callback);
+
+ // only one task per directory/file
+ std::string key = eventType + ":" + path;
+ m_events.erase(key);
+ m_events.insert(std::make_pair(key, std::move(event)));
+}
+
+void
FsWatcher::DidDirectoryChanged(QString dirPath)
{
_LOG_DEBUG("Triggered DirPath: " << dirPath.toStdString());
- filesystem::path absPathTriggeredDir(dirPath.toStdString());
- if (!filesystem::exists(filesystem::path(absPathTriggeredDir))) {
- Scheduler::scheduleOneTimeTask(m_scheduler, 0.5,
- bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this,
- dirPath),
- "r-" + dirPath.toStdString()); // only one task will be scheduled per directory
+ fs::path absPathTriggeredDir(dirPath.toStdString());
+ if (!fs::exists(fs::path(absPathTriggeredDir))) {
+ rescheduleEvent("r-", dirPath.toStdString(), time::milliseconds(500),
+ bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, dirPath));
}
else {
- // m_executor.execute (bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
- Scheduler::scheduleOneTimeTask(m_scheduler, 0.5,
- bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this,
- dirPath),
- dirPath.toStdString()); // only one task will be scheduled per directory
+ rescheduleEvent("", dirPath.toStdString(), time::milliseconds(500),
+ bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
- // m_executor.execute (bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
- Scheduler::scheduleOneTimeTask(m_scheduler, 300,
- bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this,
- dirPath),
- "rescan-" +
- dirPath.toStdString()); // only one task will be scheduled per directory
+ // rescan updated folder for updates
+ rescheduleEvent("rescan", dirPath.toStdString(), time::seconds(300),
+ bind(&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
- Scheduler::scheduleOneTimeTask(m_scheduler, 300,
- bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this,
- m_dirPath),
- "rescan-r-" +
- m_dirPath.toStdString()); // only one task will be scheduled per directory
+ // rescan whole folder for deletions
+ rescheduleEvent("rescan-r", m_dirPath.toStdString(), time::seconds(300),
+ bind(&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath));
}
}
@@ -116,29 +110,30 @@
}
QString absFilePath = filePath;
- filesystem::path absPathTriggeredFile(filePath.toStdString());
+ fs::path absPathTriggeredFile(filePath.toStdString());
filePath.remove(0, m_dirPath.size());
- filesystem::path triggeredFile(filePath.toStdString());
- if (filesystem::exists(filesystem::path(absPathTriggeredFile))) {
+ fs::path triggeredFile(filePath.toStdString());
+ if (fs::exists(fs::path(absPathTriggeredFile))) {
_LOG_DEBUG("Triggered UPDATE of file: " << triggeredFile.relative_path().generic_string());
- // m_onChange (triggeredFile.relative_path ());
+ // m_onChange(triggeredFile.relative_path());
m_watcher->removePath(absFilePath);
m_watcher->addPath(absFilePath);
- Scheduler::scheduleOneTimeTask(m_scheduler, 0.5, bind(m_onChange, triggeredFile.relative_path()),
- triggeredFile.relative_path().string());
+ rescheduleEvent("", triggeredFile.relative_path().string(), time::milliseconds(500),
+ bind(m_onChange, triggeredFile.relative_path()));
}
else {
_LOG_DEBUG("Triggered DELETE of file: " << triggeredFile.relative_path().generic_string());
- // m_onDelete (triggeredFile.relative_path ());
+ // m_onDelete(triggeredFile.relative_path());
m_watcher->removePath(absFilePath);
deleteFile(triggeredFile.relative_path());
- Scheduler::scheduleOneTimeTask(m_scheduler, 0.5, bind(m_onDelete, triggeredFile.relative_path()),
- "r-" + triggeredFile.relative_path().string());
+
+ rescheduleEvent("r", triggeredFile.relative_path().string(), time::milliseconds(500),
+ bind(m_onDelete, triggeredFile.relative_path()));
}
}
@@ -147,13 +142,13 @@
{
_LOG_TRACE(" >> ScanDirectory_NotifyUpdates_Execute");
- // exclude working only on last component, not the full path; iterating through all directories, even excluded from monitoring
+ // exclude working only on last component, not the full path; iterating through all directories,
+ // even excluded from monitoring
QRegExp exclude("^(\\.|\\.\\.|\\.chronoshare|.*~|.*\\.swp)$");
- QDirIterator dirIterator(dirPath,
- QDir::Dirs | QDir::Files | /*QDir::Hidden |*/ QDir::NoSymLinks |
- QDir::NoDotAndDotDot,
- QDirIterator::Subdirectories); // directory iterator (recursive)
+ QDirIterator dirIterator(dirPath, QDir::Dirs | QDir::Files | /*QDir::Hidden |*/ QDir::NoSymLinks |
+ QDir::NoDotAndDotDot,
+ QDirIterator::Subdirectories); // directory iterator(recursive)
// iterate through directory recursively
while (dirIterator.hasNext()) {
@@ -169,17 +164,18 @@
_LOG_DEBUG("Not excluded file/dir: " << fileInfo.absoluteFilePath().toStdString());
QString absFilePath = fileInfo.absoluteFilePath();
- // _LOG_DEBUG ("Attempt to add path to watcher: " << absFilePath.toStdString ());
+ // _LOG_DEBUG("Attempt to add path to watcher: " << absFilePath.toStdString());
m_watcher->removePath(absFilePath);
m_watcher->addPath(absFilePath);
if (fileInfo.isFile()) {
QString relFile = absFilePath;
relFile.remove(0, m_dirPath.size());
- filesystem::path aFile(relFile.toStdString());
+ fs::path aFile(relFile.toStdString());
if (
- //!m_fileState->LookupFile (aFile.relative_path ().generic_string ()) /* file does not exist there, but exists locally: added */)
+ //!m_fileState->LookupFile(aFile.relative_path().generic_string())
+ ///* file does not exist there, but exists locally: added */)
!fileExists(aFile.relative_path()) /*file does not exist in db, but exists in fs: add */) {
addFile(aFile.relative_path());
DidFileChanged(absFilePath);
@@ -187,7 +183,7 @@
}
}
else {
- // _LOG_DEBUG ("Excluded file/dir: " << fileInfo.filePath ().toStdString ());
+ // _LOG_DEBUG("Excluded file/dir: " << fileInfo.filePath().toStdString());
}
}
}
@@ -198,73 +194,71 @@
{
_LOG_DEBUG("Triggered DirPath: " << dirPath.toStdString());
- filesystem::path absPathTriggeredDir(dirPath.toStdString());
+ fs::path absPathTriggeredDir(dirPath.toStdString());
dirPath.remove(0, m_dirPath.size());
- filesystem::path triggeredDir(dirPath.toStdString());
+ fs::path triggeredDir(dirPath.toStdString());
/*
- FileItemsPtr files = m_fileState->LookupFilesInFolderRecursively (triggeredDir.relative_path ().generic_string ());
- for (std::list<FileItem>::iterator file = files->begin (); file != files->end (); file ++)
+ FileItemsPtr files = m_fileState->LookupFilesInFolderRecursively(triggeredDir.relative_path().generic_string());
+ for (std::list<FileItem>::iterator file = files->begin(); file != files->end(); file ++)
{
- filesystem::path testFile = filesystem::path (m_dirPath.toStdString ()) / file->filename ();
- _LOG_DEBUG ("Check file for deletion [" << testFile.generic_string () << "]");
+ fs::path testFile = fs::path(m_dirPath.toStdString()) / file->filename();
+ _LOG_DEBUG("Check file for deletion [" << testFile.generic_string() << "]");
- if (!filesystem::exists (testFile))
+ if (!fs::exists(testFile))
{
- if (removeIncomplete || file->is_complete ())
+ if (removeIncomplete || file->is_complete())
{
- _LOG_DEBUG ("Notifying about removed file [" << file->filename () << "]");
- m_onDelete (file->filename ());
+ _LOG_DEBUG("Notifying about removed file [" << file->filename() << "]");
+ m_onDelete(file->filename());
}
}
}
*/
- vector<string> files;
+ std::vector<std::string> files;
getFilesInDir(triggeredDir.relative_path(), files);
- for (vector<string>::iterator file = files.begin(); file != files.end(); file++) {
- filesystem::path targetFile = filesystem::path(m_dirPath.toStdString()) / *file;
- if (!filesystem::exists(targetFile)) {
+ for (std::vector<std::string>::iterator file = files.begin(); file != files.end(); file++) {
+ fs::path targetFile = fs::path(m_dirPath.toStdString()) / *file;
+ if (!fs::exists(targetFile)) {
deleteFile(*file);
m_onDelete(*file);
}
}
}
-const string INIT_DATABASE = "\
-CREATE TABLE IF NOT EXISTS \n\
- Files( \n\
- filename TEXT NOT NULL, \n\
- PRIMARY KEY (filename) \n\
-); \n\
-CREATE INDEX filename_index ON Files (filename); \n\
+const std::string INIT_DATABASE = "\
+CREATE TABLE IF NOT EXISTS \n\
+ Files( \n\
+ filename TEXT NOT NULL, \n\
+ PRIMARY KEY(filename) \n\
+); \n\
+CREATE INDEX IF NOT EXISTS filename_index ON Files(filename); \n\
";
void
FsWatcher::initFileStateDb()
{
- filesystem::path dbFolder =
- filesystem::path(m_dirPath.toStdString()) / ".chronoshare" / "fs_watcher";
- filesystem::create_directories(dbFolder);
+ fs::path dbFolder = fs::path(m_dirPath.toStdString()) / ".chronoshare" / "fs_watcher";
+ fs::create_directories(dbFolder);
int res = sqlite3_open((dbFolder / "filestate.db").string().c_str(), &m_db);
if (res != SQLITE_OK) {
- BOOST_THROW_EXCEPTION(Error::Db() << errmsg_info_str("Cannot open database: " +
- (dbFolder / "filestate.db").string()));
+ BOOST_THROW_EXCEPTION(Error("Cannot open database: " + (dbFolder / "filestate.db").string()));
}
char* errmsg = 0;
res = sqlite3_exec(m_db, INIT_DATABASE.c_str(), NULL, NULL, &errmsg);
if (res != SQLITE_OK && errmsg != 0) {
- // _LOG_TRACE ("Init \"error\": " << errmsg);
- cout << "FS-Watcher DB error: " << errmsg << endl;
+ // _LOG_TRACE("Init \"error\": " << errmsg);
+ std::cout << "FS-Watcher DB error: " << errmsg << std::endl;
sqlite3_free(errmsg);
}
}
bool
-FsWatcher::fileExists(const filesystem::path& filename)
+FsWatcher::fileExists(const fs::path& filename)
{
sqlite3_stmt* stmt;
sqlite3_prepare_v2(m_db, "SELECT * FROM Files WHERE filename = ?;", -1, &stmt, 0);
@@ -279,17 +273,17 @@
}
void
-FsWatcher::addFile(const filesystem::path& filename)
+FsWatcher::addFile(const fs::path& filename)
{
sqlite3_stmt* stmt;
- sqlite3_prepare_v2(m_db, "INSERT OR IGNORE INTO Files (filename) VALUES (?);", -1, &stmt, 0);
+ sqlite3_prepare_v2(m_db, "INSERT OR IGNORE INTO Files(filename) VALUES(?);", -1, &stmt, 0);
sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
void
-FsWatcher::deleteFile(const filesystem::path& filename)
+FsWatcher::deleteFile(const fs::path& filename)
{
sqlite3_stmt* stmt;
sqlite3_prepare_v2(m_db, "DELETE FROM Files WHERE filename = ?;", -1, &stmt, 0);
@@ -299,14 +293,14 @@
}
void
-FsWatcher::getFilesInDir(const filesystem::path& dir, vector<string>& files)
+FsWatcher::getFilesInDir(const fs::path& dir, std::vector<std::string>& files)
{
sqlite3_stmt* stmt;
sqlite3_prepare_v2(m_db, "SELECT * FROM Files WHERE filename LIKE ?;", -1, &stmt, 0);
- string dirStr = dir.string();
- ostringstream escapedDir;
- for (string::const_iterator ch = dirStr.begin(); ch != dirStr.end(); ch++) {
+ std::string dirStr = dir.string();
+ std::ostringstream escapedDir;
+ for (std::string::const_iterator ch = dirStr.begin(); ch != dirStr.end(); ch++) {
if (*ch == '%')
escapedDir << "\\%";
else
@@ -314,19 +308,22 @@
}
escapedDir << "/"
<< "%";
- string escapedDirStr = escapedDir.str();
+ std::string escapedDirStr = escapedDir.str();
sqlite3_bind_text(stmt, 1, escapedDirStr.c_str(), escapedDirStr.size(), SQLITE_STATIC);
while (sqlite3_step(stmt) == SQLITE_ROW) {
- string filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
- sqlite3_column_bytes(stmt, 0));
+ std::string filename(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)),
+ sqlite3_column_bytes(stmt, 0));
files.push_back(filename);
}
sqlite3_finalize(stmt);
}
-#if WAF
-#include "fs-watcher.cc.moc"
+} // namespace chronoshare
+} // namespace ndn
+
+#ifdef WAF
#include "fs-watcher.moc"
+// #include "fs-watcher.cpp.moc"
#endif
diff --git a/fs-watcher/fs-watcher.hpp b/fs-watcher/fs-watcher.hpp
index e68920c..d94eed8 100644
--- a/fs-watcher/fs-watcher.hpp
+++ b/fs-watcher/fs-watcher.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016, Regents of the University of California.
+ * Copyright (c) 2013-2017, Regents of the University of California.
*
* This file is part of ChronoShare, a decentralized file sharing application over NDN.
*
@@ -17,26 +17,45 @@
*
* See AUTHORS.md for complete list of ChronoShare authors and contributors.
*/
-#ifndef FS_WATCHER_H
-#define FS_WATCHER_H
-#include <boost/filesystem.hpp>
+#ifndef CHRONOSHARE_FS_WATCHER_FS_WATCHER_HPP
+#define CHRONOSHARE_FS_WATCHER_FS_WATCHER_HPP
+
+#include "db-helper.hpp"
+#include "core/chronoshare-common.hpp"
+
#include <QFileSystemWatcher>
#include <sqlite3.h>
#include <vector>
-#include "scheduler.hpp"
+#include <ndn-cxx/util/scheduler-scoped-event-id.hpp>
+#include <ndn-cxx/util/scheduler.hpp>
+
+#include <boost/asio/io_service.hpp>
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace chronoshare {
class FsWatcher : public QObject
{
Q_OBJECT
public:
- typedef boost::function<void(const boost::filesystem::path&)> LocalFile_Change_Callback;
+ class Error : public DbHelper::Error
+ {
+ public:
+ explicit Error(const std::string& what)
+ : DbHelper::Error(what)
+ {
+ }
+ };
+
+ typedef std::function<void(const boost::filesystem::path&)> LocalFile_Change_Callback;
// constructor
- FsWatcher(QString dirPath, LocalFile_Change_Callback onChange, LocalFile_Change_Callback onDelete,
- QObject* parent = 0);
+ FsWatcher(boost::asio::io_service& io, QString dirPath, LocalFile_Change_Callback onChange,
+ LocalFile_Change_Callback onDelete, QObject* parent = 0);
// destructor
~FsWatcher();
@@ -77,9 +96,13 @@
void
getFilesInDir(const boost::filesystem::path& dir, std::vector<std::string>& files);
+ void
+ rescheduleEvent(const std::string& eventType, const std::string& dirPath,
+ const time::milliseconds& period, const Scheduler::Event& callback);
+
private:
QFileSystemWatcher* m_watcher; // filesystem watcher
- SchedulerPtr m_scheduler;
+ Scheduler m_scheduler;
QString m_dirPath; // monitored path
@@ -87,6 +110,11 @@
LocalFile_Change_Callback m_onDelete;
sqlite3* m_db;
+
+ std::map<std::string, util::scheduler::ScopedEventId> m_events;
};
-#endif // FILESYSTEMWATCHER_H
+} // namespace chronoshare
+} // namespace ndn
+
+#endif // CHRONOSHARE_FS_WATCHER_FS_WATCHER_HPP
diff --git a/tests/unit-tests/fs-watcher.t.cpp b/tests/integrated-tests/fs-watcher.t.cpp
similarity index 62%
rename from tests/unit-tests/fs-watcher.t.cpp
rename to tests/integrated-tests/fs-watcher.t.cpp
index 22b1838..05f9764 100644
--- a/tests/unit-tests/fs-watcher.t.cpp
+++ b/tests/integrated-tests/fs-watcher.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2016, Regents of the University of California.
+ * Copyright (c) 2013-2017, Regents of the University of California.
*
* This file is part of ChronoShare, a decentralized file sharing application over NDN.
*
@@ -17,79 +17,129 @@
*
* See AUTHORS.md for complete list of ChronoShare authors and contributors.
*/
+
#include "fs-watcher.hpp"
+#include "test-common.hpp"
+
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/make_shared.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/thread/thread.hpp>
-#include <QtGui>
#include <fstream>
+#include <iostream>
+#include <thread>
#include <set>
+#include "fs-watcher.t.hpp"
+
using namespace std;
-using namespace boost;
namespace fs = boost::filesystem;
-BOOST_AUTO_TEST_SUITE(TestFsWatcher)
+_LOG_INIT(Test.FSWatcher);
-void
-onChange(set<string>& files, const fs::path& file)
+namespace ndn {
+namespace chronoshare {
+namespace tests {
+
+fsWatcherApp::fsWatcherApp(int& argc, char** argv)
+ : QCoreApplication(argc, argv)
{
- cerr << "onChange called" << endl;
- files.insert(file.string());
+ connect(this, SIGNAL(stopApp()), this, SLOT(quit()), Qt::QueuedConnection);
}
-void
-onDelete(set<string>& files, const fs::path& file)
-{
- files.erase(file.string());
-}
+fsWatcherApp::~fsWatcherApp() = default;
-void
-create_file(const fs::path& ph, const std::string& contents)
+class TestFSWatcherFixture : public IdentityManagementFixture
{
- std::ofstream f(ph.string().c_str());
- if (!f) {
- abort();
+public:
+ TestFSWatcherFixture()
+ : dir(fs::path(UNIT_TEST_CONFIG_PATH) / "TestFsWatcher")
+ , argc(0)
+ {
+ if (fs::exists(dir)) {
+ fs::remove_all(dir);
+ }
+
+ fs::create_directory(dir);
}
- if (!contents.empty()) {
- f << contents;
- }
-}
-void
-run(fs::path dir, FsWatcher::LocalFile_Change_Callback c, FsWatcher::LocalFile_Change_Callback d)
-{
- int x = 0;
- QCoreApplication app(x, 0);
- FsWatcher watcher(dir.string().c_str(), c, d);
- app.exec();
- sleep(100);
-}
+ ~TestFSWatcherFixture(){
+ // cleanup
+ if (fs::exists(dir)) {
+ _LOG_DEBUG("clean all");
+ fs::remove_all(dir);
+ }
+ }
+
+ void
+ advanceClocks(std::chrono::seconds delay)
+ {
+ std::chrono::milliseconds step = delay;
+ step /= 50;
+ for (int i = 0; i < 50; ++i) {
+ std::this_thread::sleep_for(step);
+ m_io.poll();
+ m_io.reset();
+ }
+ }
+
+ void
+ onChange(set<string>& files, const fs::path& file)
+ {
+ _LOG_DEBUG("on change, file: " << file);
+ files.insert(file.string());
+ }
+
+ void
+ onDelete(set<string>& files, const fs::path& file)
+ {
+ _LOG_DEBUG("on delete, file: " << file);
+ files.erase(file.string());
+ }
+
+ void
+ create_file(const fs::path& ph, const std::string& contents)
+ {
+ std::ofstream f(ph.string().c_str());
+ if (!f) {
+ abort();
+ }
+ if (!contents.empty()) {
+ f << contents;
+ }
+ }
+
+ void
+ run()
+ {
+ app = new fsWatcherApp(argc, nullptr);
+ new FsWatcher(m_io, dir.string().c_str(),
+ std::bind(&TestFSWatcherFixture::onChange, this, std::ref(files), _1),
+ std::bind(&TestFSWatcherFixture::onDelete, this, std::ref(files), _1),
+ app);
+ app->exec();
+ }
+
+public:
+ fs::path dir;
+ set<string> files;
+ int argc;
+ fsWatcherApp* app;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestFsWatcher, TestFSWatcherFixture)
BOOST_AUTO_TEST_CASE(TestFsWatcher)
{
- fs::path dir = fs::absolute(fs::path("TestFsWatcher"));
- if (fs::exists(dir)) {
- fs::remove_all(dir);
- }
-
- fs::create_directory(dir);
-
- set<string> files;
-
- FsWatcher::LocalFile_Change_Callback fileChange = boost::bind(onChange, ref(files), _1);
- FsWatcher::LocalFile_Change_Callback fileDelete = boost::bind(onDelete, ref(files), _1);
-
- thread workThread(run, dir, fileChange, fileDelete);
- //FsWatcher watcher (dir.string().c_str(), fileChange, fileDelete);
+ std::thread workThread(boost::bind(&TestFSWatcherFixture::run, this));
+ this->advanceClocks(std::chrono::seconds(2));
// ============ check create file detection ================
create_file(dir / "test.txt", "hello");
- // have to at least wait 0.5 seconds
- usleep(1000000);
+ this->advanceClocks(std::chrono::seconds(2));
// test.txt
BOOST_CHECK_EQUAL(files.size(), 1);
BOOST_CHECK(files.find("test.txt") != files.end());
@@ -101,8 +151,7 @@
string filename = boost::lexical_cast<string>(i);
create_file(subdir / filename.c_str(), boost::lexical_cast<string>(i));
}
- // have to at least wait 0.5 * 2 seconds
- usleep(1100000);
+ this->advanceClocks(std::chrono::seconds(2));
// test.txt
// sub/0..9
BOOST_CHECK_EQUAL(files.size(), 11);
@@ -117,10 +166,9 @@
fs::copy_directory(subdir, subdir1);
for (int i = 0; i < 5; i++) {
string filename = boost::lexical_cast<string>(i);
- fs::copy_file(subdir / filename.c_str(), subdir1 / filename.c_str());
+ fs::copy(subdir / filename.c_str(), subdir1 / filename.c_str());
}
- // have to at least wait 0.5 * 2 seconds
- usleep(1100000);
+ this->advanceClocks(std::chrono::seconds(2));
// test.txt
// sub/0..9
// sub1/sub2/0..4
@@ -135,7 +183,7 @@
string filename = boost::lexical_cast<string>(i);
fs::remove(subdir / filename.c_str());
}
- usleep(1100000);
+ this->advanceClocks(std::chrono::seconds(2));
// test.txt
// sub/7..9
// sub1/sub2/0..4
@@ -148,14 +196,15 @@
BOOST_CHECK(files.find("sub/" + filename) != files.end());
}
- // =================== check remove files again, remove the whole dir this time ===================
+ // =================== check remove files again, remove the whole dir this time
+ // ===================
// before remove check
for (int i = 0; i < 5; i++) {
string filename = boost::lexical_cast<string>(i);
BOOST_CHECK(files.find("sub1/sub2/" + filename) != files.end());
}
fs::remove_all(subdir1);
- usleep(1100000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK_EQUAL(files.size(), 4);
// test.txt
// sub/7..9
@@ -169,7 +218,7 @@
string filename = boost::lexical_cast<string>(i);
fs::rename(subdir / filename.c_str(), dir / filename.c_str());
}
- usleep(1100000);
+ this->advanceClocks(std::chrono::seconds(2));
// test.txt
// 7
// 8
@@ -183,33 +232,36 @@
}
create_file(dir / "add-removal-check.txt", "add-removal-check");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") != files.end());
fs::remove(dir / "add-removal-check.txt");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") == files.end());
create_file(dir / "add-removal-check.txt", "add-removal-check");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") != files.end());
fs::remove(dir / "add-removal-check.txt");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") == files.end());
create_file(dir / "add-removal-check.txt", "add-removal-check");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") != files.end());
fs::remove(dir / "add-removal-check.txt");
- usleep(1200000);
+ this->advanceClocks(std::chrono::seconds(2));
BOOST_CHECK(files.find("add-removal-check.txt") == files.end());
- // cleanup
- if (fs::exists(dir)) {
- fs::remove_all(dir);
- }
+ emit app->stopApp();
+
+ workThread.join();
}
BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace chronoshare
+} // namespace ndn
diff --git a/tests/integrated-tests/fs-watcher.t.hpp b/tests/integrated-tests/fs-watcher.t.hpp
new file mode 100644
index 0000000..40c524b
--- /dev/null
+++ b/tests/integrated-tests/fs-watcher.t.hpp
@@ -0,0 +1,42 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017, Regents of the University of California.
+ *
+ * This file is part of ChronoShare, a decentralized file sharing application over NDN.
+ *
+ * ChronoShare 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 3 of the License, or (at your option) any later version.
+ *
+ * ChronoShare 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.
+ *
+ * You should have received copies of the GNU General Public License along with
+ * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ChronoShare authors and contributors.
+ */
+
+#include <QtWidgets>
+
+namespace ndn {
+namespace chronoshare {
+namespace tests {
+
+class fsWatcherApp : public QCoreApplication
+{
+Q_OBJECT
+
+signals:
+ void
+ stopApp();
+
+public:
+ fsWatcherApp(int& argc, char** argv);
+ ~fsWatcherApp();
+};
+
+} // namespace tests
+} // namespace chronoshare
+} // namespace ndn
diff --git a/tests/unit-tests/fs-watcher-delay.t.cpp b/tests/unit-tests/fs-watcher-delay.t.cpp
deleted file mode 100644
index 0ccd5cc..0000000
--- a/tests/unit-tests/fs-watcher-delay.t.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016, Regents of the University of California.
- *
- * This file is part of ChronoShare, a decentralized file sharing application over NDN.
- *
- * ChronoShare 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 3 of the License, or (at your option) any later version.
- *
- * ChronoShare 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.
- *
- * You should have received copies of the GNU General Public License along with
- * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
- *
- * See AUTHORS.md for complete list of ChronoShare authors and contributors.
- */
-
-#include "fs-watcher.h"
-#include <boost/bind.hpp>
-#include <boost/filesystem.hpp>
-#include <boost/filesystem/fstream.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/test/unit_test.hpp>
-#include <boost/thread/thread.hpp>
-#include <QtGui>
-#include <fstream>
-#include <iostream>
-#include <set>
-
-using namespace std;
-using namespace boost;
-namespace fs = boost::filesystem;
-
-BOOST_AUTO_TEST_SUITE(TestFsWatcherDelay)
-
-void
-onChange(const fs::path& file)
-{
- cerr << "onChange called" << endl;
-}
-
-void
-onDelete(const fs::path& file)
-{
- cerr << "onDelete called" << endl;
-}
-
-void
-run(fs::path dir, FsWatcher::LocalFile_Change_Callback c, FsWatcher::LocalFile_Change_Callback d)
-{
- int x = 0;
- QCoreApplication app(x, 0);
- FsWatcher watcher(dir.string().c_str(), c, d);
- app.exec();
- sleep(100);
-}
-
-void
-SlowWrite(fs::path& file)
-{
- fs::ofstream off(file, std::ios::out);
-
- for (int i = 0; i < 10; i++) {
- off << i << endl;
- usleep(200000);
- }
-}
-
-BOOST_AUTO_TEST_CASE(TestFsWatcherDelay)
-{
- fs::path dir = fs::absolute(fs::path("TestFsWatcher"));
- if (fs::exists(dir)) {
- fs::remove_all(dir);
- }
-
- fs::create_directory(dir);
-
- FsWatcher::LocalFile_Change_Callback fileChange = boost::bind(onChange, _1);
- FsWatcher::LocalFile_Change_Callback fileDelete = boost::bind(onDelete, _1);
-
- fs::path file = dir / "test.text";
-
- thread watcherThread(run, dir, fileChange, fileDelete);
-
- thread writeThread(SlowWrite, file);
-
-
- usleep(10000000);
-
- // cleanup
- if (fs::exists(dir)) {
- fs::remove_all(dir);
- }
-
- usleep(1000000);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/wscript b/tests/wscript
index 3cb795b..e900761 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -8,32 +8,32 @@
if not bld.env['WITH_TESTS']:
return
- Logs.error("Many unit tests are temporary disabled")
-
bld(features='cxx',
- target='unit-tests-main',
- name='unit-tests-main',
- source='main.cpp',
- use='BOOST',
+ target='tests-base',
+ name='tests-base',
+ source=bld.path.ant_glob(['*.cpp'], excl='main.cpp'),
+ use='core-objects',
includes='.. .',
- defines=['BOOST_TEST_MODULE=ChronoShare Unit Tests'])
+ defines='UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % (bld.bldnode)
+ )
- unit_tests = bld.program(
- target='../unit-tests',
- features='cxx cxxprogram',
- source=bld.path.ant_glob(['*.cpp',
- 'unit-tests/dummy-forwarder.cpp',
- 'unit-tests/sync-*.t.cpp',
- 'unit-tests/action-log.t.cpp',
- 'unit-tests/object-*.t.cpp',
- 'unit-tests/fetch*.t.cpp',
- 'unit-tests/serve-and-fetch.t.cpp',
- 'unit-tests/content-server.t.cpp',
- 'unit-tests/dispatcher.t.cpp',
- ],
- excl=['main.cpp']),
- use='chronoshare core-objects unit-tests-main',
+ for module, name in {"unit": "Unit Tests",
+ "integrated": "Integrated Tests"}.items():
+ bld(target='%s-tests-main' % module,
+ name='%s-tests-main' % module,
+ features='cxx',
+ use='core-objects BOOST',
+ source='main.cpp',
+ defines=['BOOST_TEST_MODULE=%s' % name]
+ )
+
+ tests = bld.program(
+ target='../%s-tests' % module,
+ features='qt5 cxx cxxprogram',
+ moc='' if module == 'unit' else 'integrated-tests/fs-watcher.t.hpp',
+ source=bld.path.ant_glob(['%s-tests/*.cpp' % module]),
+ use='chronoshare core-objects fs-watcher tests-base %s-tests-main QT5CORE QT5WIDGETS' % module,
install_path=None,
- defines='UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % (bld.bldnode),
+ defines=['UNIT_TEST_CONFIG_PATH=\"%s/tmp-files/\"' % (bld.bldnode)],
includes='.. ../src .',
- )
+ )
diff --git a/wscript b/wscript
index 12c7f3d..34df183 100644
--- a/wscript
+++ b/wscript
@@ -118,16 +118,16 @@
)
Logs.error("Most of Chronoshare source compilation is temporary disabled")
- # fs_watcher = bld(
- # features=['qt5', 'cxx'],
- # target='fs-watcher',
- # defines='WAF',
- # source=bld.path.ant_glob('fs-watcher/*.cpp'),
- # use='chronoshare QT5CORE',
- # includes='fs-watcher',
- # export_includes='fs-watcher',
- # )
- Logs.error("fs-watcher compilation is temporary disabled")
+ fs_watcher = bld(
+ features=['qt5', 'cxx'],
+ target='fs-watcher',
+ defines='WAF',
+ moc = "fs-watcher/fs-watcher.hpp",
+ source=bld.path.ant_glob('fs-watcher/*.cpp'),
+ use='chronoshare QT5CORE',
+ includes='fs-watcher',
+ export_includes='fs-watcher',
+ )
http_server = bld(
target = "http_server",