From 58fd4fcd6aace58207dd799c421bb4c7f1a0e1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Fri, 5 Nov 2021 15:05:01 +0100 Subject: [PATCH] #3425: Fixed suspend/resumeEvents pair in DirectoryWatcher --- Foundation/src/DirectoryWatcher.cpp | 68 +++---- .../testsuite/src/DirectoryWatcherTest.cpp | 180 +++++++++++++++--- .../testsuite/src/DirectoryWatcherTest.h | 9 +- 3 files changed, 198 insertions(+), 59 deletions(-) diff --git a/Foundation/src/DirectoryWatcher.cpp b/Foundation/src/DirectoryWatcher.cpp index 1b922607e..2f05977fb 100644 --- a/Foundation/src/DirectoryWatcher.cpp +++ b/Foundation/src/DirectoryWatcher.cpp @@ -61,7 +61,7 @@ public: { return _owner; } - + virtual void run() = 0; virtual void stop() = 0; virtual bool supportsMoveEvents() const = 0; @@ -73,21 +73,21 @@ protected: size(0) { } - + ItemInfo(const ItemInfo& other): path(other.path), size(other.size), lastModified(other.lastModified) { } - + explicit ItemInfo(const File& f): path(f.path()), size(f.isFile() ? f.getSize() : 0), lastModified(f.getLastModified()) { } - + std::string path; File::FileSize size; Timestamp lastModified; @@ -104,7 +104,7 @@ protected: ++it; } } - + void compare(ItemInfoMap& oldEntries, ItemInfoMap& newEntries) { for (auto& np: newEntries) @@ -145,7 +145,7 @@ private: DirectoryWatcherStrategy(); DirectoryWatcherStrategy(const DirectoryWatcherStrategy&); DirectoryWatcherStrategy& operator = (const DirectoryWatcherStrategy&); - + DirectoryWatcher& _owner; }; @@ -163,21 +163,21 @@ public: if (!_hStopped) throw SystemException("cannot create event"); } - + ~WindowsDirectoryWatcherStrategy() { CloseHandle(_hStopped); } - + void run() { ItemInfoMap entries; scan(entries); - + DWORD filter = FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME; if (owner().eventMask() & DirectoryWatcher::DW_ITEM_MODIFIED) filter |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE; - + std::string path(owner().directory().path()); std::wstring upath; FileImpl::convertPath(path.c_str(), upath); @@ -195,7 +195,7 @@ public: } return; } - + bool stopped = false; while (!stopped) { @@ -228,21 +228,21 @@ public: catch (Poco::Exception& exc) { owner().scanError(&owner(), exc); - } + } } FindCloseChangeNotification(hChange); } - + void stop() { SetEvent(_hStopped); } - + bool supportsMoveEvents() const { return false; } - + private: HANDLE _hStopped; }; @@ -262,12 +262,12 @@ public: _fd = inotify_init(); if (_fd == -1) throw Poco::IOException("cannot initialize inotify", errno); } - + ~LinuxDirectoryWatcherStrategy() { close(_fd); } - + void run() { int mask = 0; @@ -293,7 +293,7 @@ public: owner().scanError(&owner(), exc); } } - + Poco::Buffer buffer(4096); while (!_stopped) { @@ -314,16 +314,16 @@ public: while (n > 0) { struct inotify_event* pEvent = reinterpret_cast(buffer.begin() + i); - + if (pEvent->len > 0) - { + { if (!owner().eventsSuspended()) { Poco::Path p(owner().directory().path()); p.makeDirectory(); p.setFileName(pEvent->name); Poco::File f(p.toString()); - + if ((pEvent->mask & IN_CREATE) && (owner().eventMask() & DirectoryWatcher::DW_ITEM_ADDED)) { DirectoryWatcher::DirectoryEvent ev(f, DirectoryWatcher::DW_ITEM_ADDED); @@ -351,7 +351,7 @@ public: } } } - + i += sizeof(inotify_event) + pEvent->len; n -= sizeof(inotify_event) + pEvent->len; } @@ -359,12 +359,12 @@ public: } } } - + void stop() { _stopped = true; } - + bool supportsMoveEvents() const { return true; @@ -469,11 +469,11 @@ public: DirectoryWatcherStrategy(owner) { } - + ~PollingDirectoryWatcherStrategy() { } - + void run() { ItemInfoMap entries; @@ -493,7 +493,7 @@ public: } } } - + void stop() { _stopped.set(); @@ -520,7 +520,7 @@ DirectoryWatcher::DirectoryWatcher(const std::string& path, int eventMask, int s init(); } - + DirectoryWatcher::DirectoryWatcher(const Poco::File& directory, int eventMask, int scanInterval): _directory(directory), _eventMask(eventMask), @@ -543,16 +543,16 @@ DirectoryWatcher::~DirectoryWatcher() } } - -void DirectoryWatcher::suspendEvents() + +void DirectoryWatcher::resumeEvents() { poco_assert (_eventsSuspended > 0); - + _eventsSuspended--; } -void DirectoryWatcher::resumeEvents() +void DirectoryWatcher::suspendEvents() { _eventsSuspended++; } @@ -562,7 +562,7 @@ void DirectoryWatcher::init() { if (!_directory.exists()) throw Poco::FileNotFoundException(_directory.path()); - + if (!_directory.isDirectory()) throw Poco::InvalidArgumentException("not a directory", _directory.path()); @@ -578,7 +578,7 @@ void DirectoryWatcher::init() _thread.start(*this); } - + void DirectoryWatcher::run() { _pStrategy->run(); diff --git a/Foundation/testsuite/src/DirectoryWatcherTest.cpp b/Foundation/testsuite/src/DirectoryWatcherTest.cpp index 3838d9fdb..708cca3f7 100644 --- a/Foundation/testsuite/src/DirectoryWatcherTest.cpp +++ b/Foundation/testsuite/src/DirectoryWatcherTest.cpp @@ -24,7 +24,7 @@ using Poco::DirectoryWatcher; -DirectoryWatcherTest::DirectoryWatcherTest(const std::string& name): +DirectoryWatcherTest::DirectoryWatcherTest(const std::string& name): CppUnit::TestCase(name), _error(false) { @@ -39,23 +39,23 @@ DirectoryWatcherTest::~DirectoryWatcherTest() void DirectoryWatcherTest::testAdded() { DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); - + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); - + Poco::Thread::sleep(1000); - + Poco::Path p(path()); p.setFileName("test.txt"); Poco::FileOutputStream fos(p.toString()); fos << "Hello, world!"; fos.close(); - + Poco::Thread::sleep(2000*dw.scanInterval()); - + assertTrue (_events.size() >= 1); assertTrue (_events[0].callback == "onItemAdded"); assertTrue (Poco::Path(_events[0].path).getFileName() == "test.txt"); @@ -73,20 +73,20 @@ void DirectoryWatcherTest::testRemoved() fos.close(); DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); - + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); - + Poco::Thread::sleep(1000); - + Poco::File f(p.toString()); f.remove(); - + Poco::Thread::sleep(2000*dw.scanInterval()); - + assertTrue (_events.size() >= 1); assertTrue (_events[0].callback == "onItemRemoved"); assertTrue (Poco::Path(_events[0].path).getFileName() == "test.txt"); @@ -104,21 +104,21 @@ void DirectoryWatcherTest::testModified() fos.close(); DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); - + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); - + Poco::Thread::sleep(1000); - + Poco::FileOutputStream fos2(p.toString(), std::ios::app); fos2 << "Again!"; fos2.close(); - + Poco::Thread::sleep(2000*dw.scanInterval()); - + assertTrue (_events.size() >= 1); assertTrue (_events[0].callback == "onItemModified"); assertTrue (Poco::Path(_events[0].path).getFileName() == "test.txt"); @@ -136,22 +136,22 @@ void DirectoryWatcherTest::testMoved() fos.close(); DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); - + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); - + Poco::Thread::sleep(1000); - + Poco::Path p2(path()); p2.setFileName("test2.txt"); Poco::File f(p.toString()); f.renameTo(p2.toString()); - + Poco::Thread::sleep(2000*dw.scanInterval()); - + if (dw.supportsMoveEvents()) { assertTrue (_events.size() >= 2); @@ -188,11 +188,144 @@ void DirectoryWatcherTest::testMoved() } +void DirectoryWatcherTest::testSuspend() +{ + Poco::Path p(path()); + p.setFileName("test.txt"); + Poco::FileOutputStream fos(p.toString()); + fos << "Hello, world!"; + fos.close(); + + DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); + + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); + dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); + dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); + dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); + dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); + + Poco::Thread::sleep(1000); + + dw.suspendEvents(); + + Poco::FileOutputStream fos2(p.toString(), std::ios::app); + fos2 << "Again!"; + fos2.close(); + + Poco::Thread::sleep(2000*dw.scanInterval()); + + assertTrue (_events.size() == 0); + assertTrue (!_error); +} + + +void DirectoryWatcherTest::testResume() +{ + Poco::Path p(path()); + p.setFileName("test.txt"); + Poco::FileOutputStream fos(p.toString()); + fos << "Hello, world!"; + fos.close(); + + DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); + + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); + dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); + dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); + dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); + dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); + + Poco::Thread::sleep(1000); + + dw.suspendEvents(); + + Poco::FileOutputStream fos2(p.toString(), std::ios::app); + fos2 << "Again!"; + fos2.close(); + + assertTrue (_events.size() == 0); + assertTrue (!_error); + + dw.resumeEvents(); + + Poco::FileOutputStream fos3(p.toString(), std::ios::app); + fos3 << "Now it works!"; + fos3.close(); + + Poco::Thread::sleep(2000*dw.scanInterval()); + + assertTrue (_events.size() >= 1); + assertTrue (_events[0].callback == "onItemModified"); + assertTrue (Poco::Path(_events[0].path).getFileName() == "test.txt"); + assertTrue (_events[0].type == DirectoryWatcher::DW_ITEM_MODIFIED); + assertTrue (!_error); +} + + +void DirectoryWatcherTest::testSuspendMultipleTimes() +{ + Poco::Path p(path()); + p.setFileName("test.txt"); + Poco::FileOutputStream fos(p.toString()); + fos << "Hello, world!"; + fos.close(); + + DirectoryWatcher dw(path().toString(), DirectoryWatcher::DW_FILTER_ENABLE_ALL, 2); + + dw.itemAdded += Poco::delegate(this, &DirectoryWatcherTest::onItemAdded); + dw.itemRemoved += Poco::delegate(this, &DirectoryWatcherTest::onItemRemoved); + dw.itemModified += Poco::delegate(this, &DirectoryWatcherTest::onItemModified); + dw.itemMovedFrom += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedFrom); + dw.itemMovedTo += Poco::delegate(this, &DirectoryWatcherTest::onItemMovedTo); + + Poco::Thread::sleep(1000); + + dw.suspendEvents(); + dw.suspendEvents(); + dw.suspendEvents(); + + Poco::FileOutputStream fos2(p.toString(), std::ios::app); + fos2 << "Not notified!"; + fos2.close(); + + Poco::Thread::sleep(2000*dw.scanInterval()); + + assertTrue (_events.size() == 0); + assertTrue (!_error); + + dw.resumeEvents(); + + Poco::FileOutputStream fos3(p.toString(), std::ios::app); + fos3 << "Still not notified!"; + fos3.close(); + + Poco::Thread::sleep(2000*dw.scanInterval()); + + assertTrue (_events.size() == 0); + assertTrue (!_error); + + dw.resumeEvents(); + dw.resumeEvents(); + + Poco::FileOutputStream fos4(p.toString(), std::ios::app); + fos4 << "Now it works!"; + fos4.close(); + + Poco::Thread::sleep(2000*dw.scanInterval()); + + assertTrue (_events.size() >= 1); + assertTrue (_events[0].callback == "onItemModified"); + assertTrue (Poco::Path(_events[0].path).getFileName() == "test.txt"); + assertTrue (_events[0].type == DirectoryWatcher::DW_ITEM_MODIFIED); + assertTrue (!_error); +} + + void DirectoryWatcherTest::setUp() { _error = false; _events.clear(); - + try { Poco::File d(path().toString()); @@ -292,6 +425,9 @@ CppUnit::Test* DirectoryWatcherTest::suite() CppUnit_addTest(pSuite, DirectoryWatcherTest, testRemoved); CppUnit_addTest(pSuite, DirectoryWatcherTest, testModified); CppUnit_addTest(pSuite, DirectoryWatcherTest, testMoved); + CppUnit_addTest(pSuite, DirectoryWatcherTest, testSuspend); + CppUnit_addTest(pSuite, DirectoryWatcherTest, testResume); + CppUnit_addTest(pSuite, DirectoryWatcherTest, testSuspendMultipleTimes); return pSuite; } diff --git a/Foundation/testsuite/src/DirectoryWatcherTest.h b/Foundation/testsuite/src/DirectoryWatcherTest.h index fd9ada559..556a15d87 100644 --- a/Foundation/testsuite/src/DirectoryWatcherTest.h +++ b/Foundation/testsuite/src/DirectoryWatcherTest.h @@ -35,12 +35,15 @@ public: void testRemoved(); void testModified(); void testMoved(); - + void testSuspend(); + void testResume(); + void testSuspendMultipleTimes(); + void setUp(); void tearDown(); static CppUnit::Test* suite(); - + protected: void onItemAdded(const Poco::DirectoryWatcher::DirectoryEvent& ev); void onItemRemoved(const Poco::DirectoryWatcher::DirectoryEvent& ev); @@ -48,7 +51,7 @@ protected: void onItemMovedFrom(const Poco::DirectoryWatcher::DirectoryEvent& ev); void onItemMovedTo(const Poco::DirectoryWatcher::DirectoryEvent& ev); void onError(const Poco::Exception& exc); - + Poco::Path path() const; private: