From 574a02f3e77d1405251416a49d968b1f915d7de7 Mon Sep 17 00:00:00 2001
From: Sergey Yakubov <sergey.yakubov@desy.de>
Date: Wed, 29 Aug 2018 17:00:19 +0200
Subject: [PATCH] start unit testing for inotify

---
 .../event_monitor_producer/CMakeLists.txt     |   7 +-
 .../src/inotify_linux.cpp                     |  24 ++++
 .../src/inotify_linux.h                       |  21 +++
 .../src/system_folder_watch_linux.cpp         |  16 ++-
 .../src/system_folder_watch_linux.h           |  10 +-
 .../unittests/mock_inotify.h                  |  22 +++
 .../test_system_folder_watch_linux.cpp        | 128 ++++++++++++++++++
 7 files changed, 217 insertions(+), 11 deletions(-)
 create mode 100644 producer/event_monitor_producer/src/inotify_linux.cpp
 create mode 100644 producer/event_monitor_producer/src/inotify_linux.h
 create mode 100644 producer/event_monitor_producer/unittests/mock_inotify.h
 create mode 100644 producer/event_monitor_producer/unittests/test_system_folder_watch_linux.cpp

diff --git a/producer/event_monitor_producer/CMakeLists.txt b/producer/event_monitor_producer/CMakeLists.txt
index a6b4b82e9..616cd34b0 100644
--- a/producer/event_monitor_producer/CMakeLists.txt
+++ b/producer/event_monitor_producer/CMakeLists.txt
@@ -9,7 +9,7 @@ set(SOURCE_FILES
 IF(WIN32)
     set(SOURCE_FILES ${SOURCE_FILES} src/system_folder_watch_windows.cpp)
 ELSEIF(UNIX)
-    set(SOURCE_FILES ${SOURCE_FILES} src/system_folder_watch_linux.cpp src/inotify_event.cpp src/inotify_event.h)
+    set(SOURCE_FILES ${SOURCE_FILES} src/system_folder_watch_linux.cpp src/inotify_event.cpp src/inotify_linux.cpp)
 ENDIF(WIN32)
 
 
@@ -53,6 +53,11 @@ set(TEST_SOURCE_FILES
         unittests/test_folder_event_detector.cpp
    )
 
+IF(UNIX)
+set(TEST_SOURCE_FILES ${TEST_SOURCE_FILES} unittests/test_system_folder_watch_linux.cpp)
+ENDIF(WIN32)
+
+
 set(TEST_LIBRARIES "${TARGET_NAME}")
 
 gtest(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}" ${CMAKE_CURRENT_SOURCE_DIR}/src/eventmon_main.cpp)
diff --git a/producer/event_monitor_producer/src/inotify_linux.cpp b/producer/event_monitor_producer/src/inotify_linux.cpp
new file mode 100644
index 000000000..6cf907c8e
--- /dev/null
+++ b/producer/event_monitor_producer/src/inotify_linux.cpp
@@ -0,0 +1,24 @@
+#include "inotify_linux.h"
+
+#include <sys/inotify.h>
+
+
+namespace asapo {
+
+int Inotify::Init() {
+    return inotify_init();
+}
+int Inotify::AddWatch(int fd, const char* name, uint32_t mask) {
+    return inotify_add_watch(fd, name, mask);
+}
+int Inotify::DeleteWatch(int fd, int wd) {
+    return inotify_rm_watch(fd, wd);
+}
+
+
+ssize_t Inotify::Read(int fd, void* buf, size_t nbytes) {
+    return read(fd, buf, nbytes);
+}
+
+
+}
\ No newline at end of file
diff --git a/producer/event_monitor_producer/src/inotify_linux.h b/producer/event_monitor_producer/src/inotify_linux.h
new file mode 100644
index 000000000..195e86580
--- /dev/null
+++ b/producer/event_monitor_producer/src/inotify_linux.h
@@ -0,0 +1,21 @@
+#ifndef ASAPO_INOTIFY_H
+#define ASAPO_INOTIFY_H
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include "preprocessor/definitions.h"
+
+
+namespace asapo {
+
+class Inotify {
+  public:
+    VIRTUAL int Init();
+    VIRTUAL int AddWatch(int fd, const char* name, uint32_t mask);
+    VIRTUAL int DeleteWatch(int fd, int wd);
+    VIRTUAL ssize_t Read(int fd, void* buf, size_t nbytes);
+};
+
+}
+#endif //ASAPO_INOTIFY_H
diff --git a/producer/event_monitor_producer/src/system_folder_watch_linux.cpp b/producer/event_monitor_producer/src/system_folder_watch_linux.cpp
index 956a4c82e..b5782b5f3 100644
--- a/producer/event_monitor_producer/src/system_folder_watch_linux.cpp
+++ b/producer/event_monitor_producer/src/system_folder_watch_linux.cpp
@@ -3,11 +3,12 @@
 
 #include "event_monitor_error.h"
 #include "eventmon_logger.h"
+#include "io/io_factory.h"
 
 namespace asapo {
 
 Error SystemFolderWatch::AddFolderToWatch(std::string folder) {
-    int id = inotify_add_watch(watch_fd_, folder.c_str(), kInotifyWatchFlags);
+    int id = inotify__->AddWatch(watch_fd_, folder.c_str(), kInotifyWatchFlags);
     if (id == -1) {
         return EventMonitorErrorTemplates::kSystemError.Generate("cannot add watch for " + folder);
     }
@@ -23,7 +24,7 @@ Error SystemFolderWatch::AddFolderAndSubfoldersToWatch(std::string folder) {
     if (err) {
         return err;
     }
-    auto subdirs = io_-> GetSubDirectories(folder, &err);
+    auto subdirs = io__-> GetSubDirectories(folder, &err);
     if (err) {
         return err;
     }
@@ -40,7 +41,7 @@ Error SystemFolderWatch::AddFolderAndSubfoldersToWatch(std::string folder) {
 
 Error SystemFolderWatch::StartFolderMonitor(const std::string& root_folder,
                                             const std::vector<std::string>& monitored_folders) {
-    watch_fd_ = inotify_init();
+    watch_fd_ = inotify__->Init();
     if (watch_fd_ == -1) {
         return EventMonitorErrorTemplates::kSystemError.Generate("cannot initialize inotify");
     }
@@ -93,7 +94,7 @@ Error SystemFolderWatch::ProcessInotifyEvent(const InotifyEvent& event, FileEven
             oldpath += std::string("/") + event.Name();
             for (auto val = watched_folders_paths_.begin(); val != watched_folders_paths_.end();) {
                 if ((oldpath.size() <= val->second.size()) && std::equal(oldpath.begin(), oldpath.end(), val->second.begin())) {
-                    inotify_rm_watch(val->first, watch_fd_);
+                    inotify__->DeleteWatch(val->first, watch_fd_);
                     GetDefaultEventMonLogger()->Debug("removed folder from monitor: " + val->second);
                     val = watched_folders_paths_.erase(val);
                 } else {
@@ -103,7 +104,7 @@ Error SystemFolderWatch::ProcessInotifyEvent(const InotifyEvent& event, FileEven
             }
 
         } else {
-            inotify_rm_watch(it->first, watch_fd_);
+            inotify__->DeleteWatch(it->first, watch_fd_);
             watched_folders_paths_.erase(it);
             GetDefaultEventMonLogger()->Debug("removed folder from monitor: " + oldpath);
         }
@@ -130,7 +131,7 @@ Error SystemFolderWatch::ProcessInotifyEvent(const InotifyEvent& event, FileEven
 }
 
 Error SystemFolderWatch::ReadInotifyEvents(int* bytes_read) {
-    *bytes_read = read(watch_fd_, buffer, sizeof(buffer));
+    *bytes_read = inotify__->Read(watch_fd_, buffer, sizeof(buffer));
     if (*bytes_read < 0) {
         return EventMonitorErrorTemplates::kSystemError.Generate("read from inotify fd");
     }
@@ -173,5 +174,8 @@ FileEvents SystemFolderWatch::GetFileEventList(Error* err) {
     }
     return events;
 }
+SystemFolderWatch::SystemFolderWatch() : io__{GenerateDefaultIO()}, inotify__{new Inotify()} {
+
+}
 
 }
\ No newline at end of file
diff --git a/producer/event_monitor_producer/src/system_folder_watch_linux.h b/producer/event_monitor_producer/src/system_folder_watch_linux.h
index dee18bf28..a44205808 100644
--- a/producer/event_monitor_producer/src/system_folder_watch_linux.h
+++ b/producer/event_monitor_producer/src/system_folder_watch_linux.h
@@ -4,16 +4,16 @@
 #include <vector>
 #include <string>
 #include <map>
+#include <unistd.h>
 
 #include "common/error.h"
 #include "preprocessor/definitions.h"
 #include "asapo_producer.h"
 #include "common.h"
 #include "io/io.h"
-#include "io/io_factory.h"
-#include <sys/inotify.h>
-#include <unistd.h>
 #include "inotify_event.h"
+#include "inotify_linux.h"
+
 
 namespace asapo {
 
@@ -34,10 +34,12 @@ class SystemFolderWatch {
   public:
     VIRTUAL Error StartFolderMonitor(const std::string& root_folder, const std::vector<std::string>& monitored_folders);
     VIRTUAL FileEvents GetFileEventList(Error* err);
+    SystemFolderWatch();
+    std::unique_ptr<IO> io__;
+    std::unique_ptr<Inotify> inotify__;
   private:
     Error AddFolderAndSubfoldersToWatch(std::string folder);
     Error AddFolderToWatch(std::string folder);
-    std::unique_ptr<IO> io_{GenerateDefaultIO()};
     Error ProcessInotifyEvent(const InotifyEvent& event, FileEvents* file_events);
   private:
     char buffer[kBufLen]  __attribute__ ((aligned(8)));
diff --git a/producer/event_monitor_producer/unittests/mock_inotify.h b/producer/event_monitor_producer/unittests/mock_inotify.h
new file mode 100644
index 000000000..54ab163f0
--- /dev/null
+++ b/producer/event_monitor_producer/unittests/mock_inotify.h
@@ -0,0 +1,22 @@
+#ifndef ASAPO_MOCKSYSTEMFOLDERWATCH_H
+#define ASAPO_MOCKSYSTEMFOLDERWATCH_H
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "../src/inotify_linux.h"
+
+namespace asapo {
+
+class MockInotify : public Inotify {
+  public:
+    MOCK_METHOD0(Init, int ());
+    MOCK_METHOD3(AddWatch, int (int,const char*,uint32_t));
+    MOCK_METHOD2(DeleteWatch, int (int,int));
+    MOCK_METHOD3(Read, ssize_t (int,void*,size_t));
+};
+
+}
+
+
+#endif //ASAPO_MOCKSYSTEMFOLDERWATCH_H
diff --git a/producer/event_monitor_producer/unittests/test_system_folder_watch_linux.cpp b/producer/event_monitor_producer/unittests/test_system_folder_watch_linux.cpp
new file mode 100644
index 000000000..c4d8bcf04
--- /dev/null
+++ b/producer/event_monitor_producer/unittests/test_system_folder_watch_linux.cpp
@@ -0,0 +1,128 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "../src/system_folder_watch_linux.h"
+#include "../src/event_monitor_error.h"
+#include "../src/common.h"
+
+#include "mock_inotify.h"
+#include <unittests/MockIO.h>
+
+
+
+using ::testing::Return;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::SetArgReferee;
+using ::testing::SetArgPointee;
+using ::testing::Gt;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::Mock;
+using ::testing::InSequence;
+using ::testing::HasSubstr;
+using testing::StrEq;
+
+using ::asapo::Error;
+using ::asapo::ErrorInterface;
+using asapo::FileEvents;
+using asapo::FileEvent;
+using asapo::SystemFolderWatch;
+
+namespace {
+
+
+TEST(SystemFolderWatch, Constructor) {
+    SystemFolderWatch watch;
+    ASSERT_THAT(dynamic_cast<asapo::IO*>(watch.io__.get()), Ne(nullptr));
+    ASSERT_THAT(dynamic_cast<asapo::Inotify*>(watch.inotify__.get()), Ne(nullptr));
+}
+
+class SystemFolderWatchTests : public testing::Test {
+ public:
+  Error err;
+  ::testing::NiceMock<asapo::MockInotify> mock_inotify;
+  ::testing::NiceMock<asapo::MockIO> mock_io;
+  SystemFolderWatch watch{};
+  std::string expected_root_folder = "/tmp";
+  std::vector<std::string> expected_folders{"test1", "test2"};
+  std::vector<std::string> expected_subfolders1{"/tmp/test1/sub11"};
+  std::vector<std::string> expected_subfolders2{"/tmp/test2/sub21","/tmp/test2/sub22"};
+  std::vector<std::string> expected_watches{"/tmp/test1", "/tmp/test2","/tmp/test1/sub11","/tmp/test2/sub21","/tmp/test2/sub22"};
+  int expected_wd = 1;
+  int expected_fd = 1;
+  void MockStartMonitoring();
+  void SetUp() override {
+      watch.inotify__ = std::unique_ptr<asapo::Inotify> {&mock_inotify};
+      watch.io__ = std::unique_ptr<asapo::IO> {&mock_io};
+  }
+  void TearDown() override {
+      watch.inotify__.release();
+      watch.io__.release();
+  }
+};
+
+void SystemFolderWatchTests::MockStartMonitoring() {
+    EXPECT_CALL(mock_inotify, Init())
+        .WillOnce(
+            Return(expected_wd)
+        );
+    EXPECT_CALL(mock_io, GetSubDirectories_t(expected_root_folder+"/"+expected_folders[0],_))
+        .WillOnce(
+            Return(expected_subfolders1)
+        );
+    EXPECT_CALL(mock_io, GetSubDirectories_t(expected_root_folder+"/"+expected_folders[1],_))
+        .WillOnce(
+            Return(expected_subfolders2)
+        );
+
+    for (auto& watch  : expected_watches) {
+        EXPECT_CALL(mock_inotify,AddWatch(expected_wd,StrEq(watch),asapo::kInotifyWatchFlags))
+            .WillOnce(
+                Return(expected_fd)
+            );
+    }
+
+    auto err = watch.StartFolderMonitor(expected_root_folder,expected_folders);
+    ASSERT_THAT(err, Eq(nullptr));
+
+
+    Mock::VerifyAndClearExpectations(&mock_inotify);
+}
+
+TEST_F(SystemFolderWatchTests, ErrorInitInotifyStartMonitoring) {
+    EXPECT_CALL(mock_inotify, Init())
+        .WillOnce(
+            Return(-1)
+        );
+
+    auto err = watch.StartFolderMonitor(expected_root_folder,expected_folders);
+    ASSERT_THAT(err, Eq(asapo::EventMonitorErrorTemplates::kSystemError));
+}
+
+
+TEST_F(SystemFolderWatchTests, OKInitInotifyStartMonitoring) {
+    MockStartMonitoring();
+}
+
+
+TEST_F(SystemFolderWatchTests, ErrorGetSubdirsStartMonitoring) {
+    EXPECT_CALL(mock_inotify, Init())
+        .WillOnce(
+            Return(expected_wd)
+        );
+    EXPECT_CALL(mock_io, GetSubDirectories_t(expected_root_folder+"/"+expected_folders[0],_))
+        .WillOnce(DoAll(
+            SetArgPointee<1>(asapo::IOErrorTemplates::kUnknownIOError.Generate().release()),
+            Return(asapo::SubDirList{}))
+        );
+
+    auto err = watch.StartFolderMonitor(expected_root_folder,expected_folders);
+    ASSERT_THAT(err, Eq(asapo::EventMonitorErrorTemplates::kSystemError));
+}
+
+
+
+}
+
+
-- 
GitLab