diff --git a/CMakeModules/astyle.cmake b/CMakeModules/astyle.cmake index ad5a7695f4afec21ad931355c7c27888b02acea7..326e6697b81b986f884727ea55e2b4b937db931d 100644 --- a/CMakeModules/astyle.cmake +++ b/CMakeModules/astyle.cmake @@ -6,8 +6,8 @@ if(ASTYLE_EXECUTABLE) COMMAND ${ASTYLE_EXECUTABLE} -i --exclude=${PROJECT_BINARY_DIR} - --recursive -n --style=google --indent=spaces=4 - --max-instatement-indent=120 + --recursive -n --style=google --indent=spaces=4 --max-code-length=120 + --max-instatement-indent=50 --pad-oper --align-pointer=type "${PROJECT_SOURCE_DIR}/*.cpp" "${PROJECT_SOURCE_DIR}/*.h" WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} VERBATIM diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt index 9d5e62bad1e9f35a06bb073d13631eec038c7b67..1549c800f3fb0c0b0296ce96ea42b6f6725d51e9 100644 --- a/common/cpp/CMakeLists.txt +++ b/common/cpp/CMakeLists.txt @@ -4,8 +4,12 @@ set(SOURCE_FILES include/common/networking.h include/system_wrappers/io.h include/system_wrappers/system_io.h - src/system_io.cpp - src/system_io_linux.cpp) + src/system_io.cpp) +IF(WIN32) + # Do windows specific includes +ELSEIF(UNIX) + set(SOURCE_FILES ${SOURCE_FILES} src/system_io_linux.cpp) +ENDIF(WIN32) ################################ diff --git a/common/cpp/include/system_wrappers/io.h b/common/cpp/include/system_wrappers/io.h index 371bae4ffe6b470c44940ca38ad75cda4a9c966a..ac35b38f92bdda64d19bbdb9b366530b7efc5c2c 100644 --- a/common/cpp/include/system_wrappers/io.h +++ b/common/cpp/include/system_wrappers/io.h @@ -3,10 +3,17 @@ #include <string> #include <vector> - +#include <chrono> namespace hidra2 { +struct FileInfo { + std::string base_name; + std::string relative_path; + std::chrono::system_clock::time_point modify_date; +}; + + enum class IOErrors { NO_ERROR, FOLDER_NOT_FOUND, @@ -16,13 +23,13 @@ enum class IOErrors { class IO { public: - virtual int open(const char *__file, int __oflag) = 0; + virtual int open(const char* __file, int __oflag) = 0; virtual int close(int __fd) = 0; - virtual ssize_t read(int __fd, void *buf, size_t count) = 0; - virtual ssize_t write(int __fd, const void *__buf, size_t __n) = 0; + virtual ssize_t read(int __fd, void* buf, size_t count) = 0; + virtual ssize_t write(int __fd, const void* __buf, size_t __n) = 0; // this is not standard function - to be implemented differently in windows and linux - virtual std::vector<std::string> FilesInFolder(std::string folder,IOErrors* err) = 0; + virtual std::vector<FileInfo> FilesInFolder(const std::string& folder, IOErrors* err) = 0; }; } diff --git a/common/cpp/include/system_wrappers/system_io.h b/common/cpp/include/system_wrappers/system_io.h index 166068dd7cd493f4a82c721db9462fe551ec3c3f..def69d20638933afa42b9cc7f53781774768e8a6 100644 --- a/common/cpp/include/system_wrappers/system_io.h +++ b/common/cpp/include/system_wrappers/system_io.h @@ -4,13 +4,14 @@ #include "io.h" namespace hidra2 { + class SystemIO final : public IO { public: - int open(const char *__file, int __oflag); + int open(const char* __file, int __oflag); int close(int __fd); - ssize_t read(int __fd, void *buf, size_t count); - ssize_t write(int __fd, const void *__buf, size_t __n); - std::vector<std::string> FilesInFolder(std::string folder,IOErrors* err); + ssize_t read(int __fd, void* buf, size_t count); + ssize_t write(int __fd, const void* __buf, size_t __n); + std::vector<FileInfo> FilesInFolder(const std::string& folder, IOErrors* err); }; } diff --git a/common/cpp/src/system_io.cpp b/common/cpp/src/system_io.cpp index 96bee71161ea1ac6921893af43524917cd889cf4..34a78141ea7cc178f52d0eafd7a9c9246bd2783b 100644 --- a/common/cpp/src/system_io.cpp +++ b/common/cpp/src/system_io.cpp @@ -2,7 +2,7 @@ #include <unistd.h> #include <system_wrappers/system_io.h> -int hidra2::SystemIO::open(const char *__file, int __oflag) { +int hidra2::SystemIO::open(const char* __file, int __oflag) { return ::open(__file, __oflag); } @@ -10,10 +10,10 @@ int hidra2::SystemIO::close(int __fd) { return ::close(__fd); } -ssize_t hidra2::SystemIO::read(int __fd, void *buf, size_t count) { +ssize_t hidra2::SystemIO::read(int __fd, void* buf, size_t count) { return ::read(__fd, buf, count); } -ssize_t hidra2::SystemIO::write(int __fd, const void *__buf, size_t __n) { +ssize_t hidra2::SystemIO::write(int __fd, const void* __buf, size_t __n) { return ::write(__fd, __buf, __n); } diff --git a/common/cpp/src/system_io_linux.cpp b/common/cpp/src/system_io_linux.cpp index 21e4edfad3d6f13ff36491ea8bc14095a8a1ff8e..7ae3486c262d77215ea6f82991f42ea91b2b7442 100644 --- a/common/cpp/src/system_io_linux.cpp +++ b/common/cpp/src/system_io_linux.cpp @@ -1,10 +1,127 @@ #include "system_wrappers/system_io.h" +#include <cstring> + +#include <dirent.h> +#include <sys/stat.h> +#include <algorithm> + +using std::string; +using std::vector; +using std::chrono::system_clock; + namespace hidra2 { -std::vector<std::string> SystemIO::FilesInFolder(std::string folder, IOErrors* err) { - *err=IOErrors::FOLDER_NOT_FOUND; - return {}; +IOErrors IOErrorFromErrno() { + IOErrors err; + switch (errno) { + case 0: + err = IOErrors::NO_ERROR; + break; + case ENOENT: + case ENOTDIR: + err = IOErrors::FOLDER_NOT_FOUND; + break; + case EACCES: + err = IOErrors::PERMISSIONS_DENIED; + break; + default: + err = IOErrors::UNKWOWN_ERROR; + break; + } + return err; } -} \ No newline at end of file +bool IsDirectory(const struct dirent* entity) { + return entity->d_type == DT_DIR && + strstr(entity->d_name, "..") == nullptr && + strstr(entity->d_name, ".") == nullptr; +} + +system_clock::time_point GetTimePointFromFile(const string& fname, IOErrors* err) { + + struct stat t_stat; + int res = stat(fname.c_str(), &t_stat); + if (res < 0) { + *err = IOErrorFromErrno(); + return system_clock::time_point{}; + } + + std::chrono::nanoseconds d = std::chrono::nanoseconds{t_stat.st_mtim.tv_nsec} + + std::chrono::seconds{t_stat.st_mtim.tv_sec}; + return system_clock::time_point {std::chrono::duration_cast<system_clock::duration>(d)}; +} + +void ProcessFileEntity(const struct dirent* entity, const std::string& path, + std::vector<FileInfo>& files, IOErrors* err) { + + *err = IOErrors::NO_ERROR; + if (entity->d_type != DT_REG) { + return; + } + FileInfo file_info; + file_info.relative_path = path; + file_info.base_name = entity->d_name; + + file_info.modify_date = GetTimePointFromFile(path + "/" + entity->d_name, err); + if (*err != IOErrors::NO_ERROR) { + return; + } + + files.push_back(file_info); +} + +void CollectFileInformationRecursivly(const std::string& path, + std::vector<FileInfo>& files, IOErrors* err) { + auto dir = opendir((path).c_str()); + if (dir == nullptr) { + *err = IOErrorFromErrno(); + return; + } + + while (struct dirent* current_entity = readdir(dir)) { + if (IsDirectory(current_entity)) { + CollectFileInformationRecursivly(path + "/" + current_entity->d_name, + files, err); + if (*err != IOErrors::NO_ERROR) { + closedir(dir); + return; + } + } + ProcessFileEntity(current_entity, path, files, err); + if (*err != IOErrors::NO_ERROR) { + closedir(dir); + return; + } + + } + *err = IOErrorFromErrno(); + closedir(dir); +} + +void SortFileList(std::vector<FileInfo>& file_list) { + std::sort(file_list.begin(), file_list.end(), + [](FileInfo const & a, FileInfo const & b) { + return a.modify_date < b.modify_date; + }); +} + +void StripBasePath(const string& folder, std::vector<FileInfo>& file_list) { + auto n_erase = folder.size() + 1; + for (auto& file : file_list) { + file.relative_path.erase(0, n_erase); + } +} + +std::vector<FileInfo> SystemIO::FilesInFolder(const string& folder, IOErrors* err) { + std::vector<FileInfo> files{}; + CollectFileInformationRecursivly(folder, files, err); + if (*err != IOErrors::NO_ERROR) { + return {}; + } + StripBasePath(folder, files); + SortFileList(files); + return files; +} + +} diff --git a/producer/api/src/producer/producer.cpp b/producer/api/src/producer/producer.cpp index 15a6fca30e57b034984ae3494c1ab9a4471c0341..f4373a91db6b1098bc85595a3567f7b3de5577df 100644 --- a/producer/api/src/producer/producer.cpp +++ b/producer/api/src/producer/producer.cpp @@ -7,6 +7,6 @@ hidra2::Producer::Producer() { kinit_count_++; } -hidra2::Producer *hidra2::Producer::CreateProducer(std::string receiver_address) { +hidra2::Producer* hidra2::Producer::CreateProducer(std::string receiver_address) { return new Producer(); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0c4ded43ff53b6aeca58ee5114f4b765748c2af5..a72ee6770e8f439e65649143a3bc5621538ab578 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,4 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.7) # needed for fixtures add_subdirectory(system_io) diff --git a/tests/system_io/CMakeLists.txt b/tests/system_io/CMakeLists.txt index ed34b6946b1de98aa58d9e7d32ba570b4591944a..15ed86b249c298c981a95208180bd76dde36003c 100644 --- a/tests/system_io/CMakeLists.txt +++ b/tests/system_io/CMakeLists.txt @@ -13,5 +13,33 @@ set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) # Testing ################################ -add_test(NAME test-${TARGET_NAME} COMMAND ${TARGET_NAME} test notfound) -set_tests_properties(test-${TARGET_NAME} PROPERTIES LABELS "integration;all") +add_test(NAME test-${TARGET_NAME}-create_list COMMAND ${TARGET_NAME} test 23subtest/subtest241) +add_test(NAME test-${TARGET_NAME}-foldernotfound COMMAND ${TARGET_NAME} test_notexist notfound) +add_test(NAME test-${TARGET_NAME}-foldernoaccess COMMAND ${TARGET_NAME} test_noaccess noaccess) + + +set_tests_properties(test-${TARGET_NAME}-create_list PROPERTIES + LABELS "integration;all" + FIXTURES_REQUIRED ${TARGET_NAME}-fixture + ) + +set_tests_properties(test-${TARGET_NAME}-foldernotfound PROPERTIES + LABELS "integration;all" +# FIXTURES_REQUIRED ${TARGET_NAME}-fixture + ) + + +set_tests_properties(test-${TARGET_NAME}-foldernoaccess PROPERTIES + LABELS "integration;all" + FIXTURES_REQUIRED ${TARGET_NAME}-fixture + ) + + +add_test(NAME test-${TARGET_NAME}-setup COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/setup.sh) +add_test(NAME test-${TARGET_NAME}-cleanup COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/cleanup.sh) +set_tests_properties(test-${TARGET_NAME}-setup PROPERTIES FIXTURES_SETUP ${TARGET_NAME}-fixture) +set_tests_properties(test-${TARGET_NAME}-cleanup PROPERTIES FIXTURES_CLEANUP ${TARGET_NAME}-fixture) + + + + diff --git a/tests/system_io/cleanup.sh b/tests/system_io/cleanup.sh new file mode 100644 index 0000000000000000000000000000000000000000..dbb103528e3ce786766e76856b28f88906ad1b36 --- /dev/null +++ b/tests/system_io/cleanup.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +rm -rf test +rm -rf test_noaccess diff --git a/tests/system_io/read_folder_content.cpp b/tests/system_io/read_folder_content.cpp index 0048fe286c1a3ceb502de2ca1e3cfb9e1c032291..cb4b24c10e4babb8fff403aca9c4503c61b9b45b 100644 --- a/tests/system_io/read_folder_content.cpp +++ b/tests/system_io/read_folder_content.cpp @@ -1,43 +1,49 @@ #include <iostream> -#include <string> +#include <memory> #include <system_wrappers/system_io.h> using hidra2::SystemIO; using hidra2::IOErrors; -# - -void M_Assert(bool expr, std::string expected, std::string got, std::string msg) { - if (!expr) { - std::cerr << "Assert failed:\t" << msg << "\n" +void M_AssertEq(const std::string& expected, const std::string& got) { + if (expected.compare(got) != 0) { + std::cerr << "Assert failed:\n" << "Expected:\t'" << expected << "'\n" << "Obtained:\t'" << got << "'\n"; abort(); } } - int main(int argc, char* argv[]) { - auto io = new SystemIO; - if (argc != 3) { std::cout << "Wrong number of arguments" << std::endl; return 1; } - std::string expect{argv[2]}; IOErrors err; - + auto io = std::unique_ptr<SystemIO> {new SystemIO}; auto files = io->FilesInFolder(argv[1], &err); + std::string result; + switch (err) { case IOErrors::FOLDER_NOT_FOUND: - M_Assert(expect.compare("notfound")==0,expect,"notfound","Folder not found"); - return 0; + result = "notfound"; + break; + case IOErrors::NO_ERROR: + for(auto file_info : files) + result += file_info.relative_path + file_info.base_name; + break; + case IOErrors::PERMISSIONS_DENIED: + result = "noaccess"; + break; } - return 1; + M_AssertEq(expect, result); + + + return 0; } diff --git a/tests/system_io/setup.sh b/tests/system_io/setup.sh new file mode 100644 index 0000000000000000000000000000000000000000..ad7a62176e0a0968a41a6b7a21dc9730f27b5092 --- /dev/null +++ b/tests/system_io/setup.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +mkdir -p test/subtest/subtest2 +touch test/2 +sleep 0.01 +touch test/3 +sleep 0.01 +touch test/subtest/subtest2/4 +sleep 0.01 +touch test/1 + +mkdir test_noaccess +chmod -rx test_noaccess + diff --git a/worker/api/cpp/include/worker/data_broker.h b/worker/api/cpp/include/worker/data_broker.h index 7898a8b067cf9a087da3f97432d9c9b3dc2c5be6..38a263f36d7c23714b79915228cfa20fc640f628 100644 --- a/worker/api/cpp/include/worker/data_broker.h +++ b/worker/api/cpp/include/worker/data_broker.h @@ -17,12 +17,12 @@ enum class WorkerErrorCode { class DataBroker { public: - virtual WorkerErrorCode Connect()=0; + virtual WorkerErrorCode Connect() = 0; }; class DataBrokerFactory { public: - static std::unique_ptr<DataBroker> Create(const std::string &source_name, WorkerErrorCode* return_code) noexcept; + static std::unique_ptr<DataBroker> Create(const std::string& source_name, WorkerErrorCode* return_code) noexcept; }; } diff --git a/worker/api/cpp/src/data_broker.cpp b/worker/api/cpp/src/data_broker.cpp index e99844ff9d1d5cc47e2fce31dfc7d764a7e22ee4..f412eaf1c6007d861d70d5fca99455a8c7ade33c 100644 --- a/worker/api/cpp/src/data_broker.cpp +++ b/worker/api/cpp/src/data_broker.cpp @@ -4,7 +4,7 @@ namespace hidra2 { std::unique_ptr<DataBroker> DataBrokerFactory::Create(const std::string& source_name, - WorkerErrorCode* return_code) noexcept { + WorkerErrorCode* return_code) noexcept { if (source_name.empty()) { *return_code = WorkerErrorCode::ERR__EMPTY_DATASOURCE; diff --git a/worker/api/cpp/src/folder_data_broker.cpp b/worker/api/cpp/src/folder_data_broker.cpp index 1dc55593616b4c5ab7c454f6cc6c6b96cc7afda1..e464ac107ea05d652c2a133bc371c3d2078bba1d 100644 --- a/worker/api/cpp/src/folder_data_broker.cpp +++ b/worker/api/cpp/src/folder_data_broker.cpp @@ -9,15 +9,15 @@ namespace hidra2 { WorkerErrorCode MapIOError(IOErrors io_err) { WorkerErrorCode err; switch (io_err) { // we do not use map due to performance reasons - case IOErrors::NO_ERROR: - err = WorkerErrorCode::ERR__NO_ERROR; - break; - case IOErrors::FOLDER_NOT_FOUND: - err = WorkerErrorCode::SOURCE_NOT_FOUND; - break; - default: - err = WorkerErrorCode::UNKNOWN_IO_ERROR; - break; + case IOErrors::NO_ERROR: + err = WorkerErrorCode::ERR__NO_ERROR; + break; + case IOErrors::FOLDER_NOT_FOUND: + err = WorkerErrorCode::SOURCE_NOT_FOUND; + break; + default: + err = WorkerErrorCode::UNKNOWN_IO_ERROR; + break; } return err; diff --git a/worker/api/cpp/src/folder_data_broker.h b/worker/api/cpp/src/folder_data_broker.h index ed3010a23ab1218ea03b97e2570bc1e35359cdd9..bdecc943d2fce54262c8ca16c0a98d496c102190 100644 --- a/worker/api/cpp/src/folder_data_broker.h +++ b/worker/api/cpp/src/folder_data_broker.h @@ -17,7 +17,7 @@ class FolderDataBroker final : public hidra2::DataBroker { std::unique_ptr<hidra2::IO> io__; // modified in testings to mock system calls,otherwise do not touch private: std::string base_path_; - std::vector<std::string> filelist_; + std::vector<FileInfo> filelist_; }; } diff --git a/worker/api/cpp/unittests/test_folder_broker.cpp b/worker/api/cpp/unittests/test_folder_broker.cpp index f9b9b802ab6d7a9959eed1c3436f23e9fa831281..b4b3ef926360e8a4bf22591d27c1219b96f7b651 100644 --- a/worker/api/cpp/unittests/test_folder_broker.cpp +++ b/worker/api/cpp/unittests/test_folder_broker.cpp @@ -13,6 +13,7 @@ using hidra2::FolderDataBroker; using hidra2::WorkerErrorCode; using hidra2::IO; using hidra2::IOErrors; +using hidra2::FileInfo; using ::testing::Eq; using ::testing::Ne; @@ -21,7 +22,7 @@ using ::testing::Test; namespace { TEST(FolderDataBroker, SetCorrectIO) { - auto data_broker=new FolderDataBroker("test"); + auto data_broker = new FolderDataBroker("test"); ASSERT_THAT(dynamic_cast<hidra2::SystemIO*>(data_broker->io__.release()), Ne(nullptr)); } @@ -29,36 +30,36 @@ TEST(FolderDataBroker, SetCorrectIO) { class FakeIO: public IO { public: - int open(const char *__file, int __oflag) { + int open(const char* __file, int __oflag) { return 0; }; int close(int __fd) { return 0; }; - ssize_t read(int __fd, void *buf, size_t count) { + ssize_t read(int __fd, void* buf, size_t count) { return 0; }; - ssize_t write(int __fd, const void *__buf, size_t __n) { + ssize_t write(int __fd, const void* __buf, size_t __n) { return 0; }; - std::vector<std::string> FilesInFolder(std::string folder, IOErrors* err) { - *err=IOErrors::NO_ERROR; + std::vector<FileInfo> FilesInFolder(const std::string& folder, IOErrors* err) { + *err = IOErrors::NO_ERROR; return {}; } }; class IOFolderNotFound: public FakeIO { public: - std::vector<std::string> FilesInFolder(std::string folder, IOErrors* err) { - *err=IOErrors::FOLDER_NOT_FOUND; + std::vector<FileInfo> FilesInFolder(const std::string& folder, IOErrors* err) { + *err = IOErrors::FOLDER_NOT_FOUND; return {}; } }; class IOFodlerUnknownError: public FakeIO { public: - std::vector<std::string> FilesInFolder(std::string folder, IOErrors* err) { - *err=IOErrors::UNKWOWN_ERROR; + std::vector<FileInfo> FilesInFolder(const std::string& folder, IOErrors* err) { + *err = IOErrors::UNKWOWN_ERROR; return {}; } }; @@ -69,7 +70,7 @@ class FolderDataBrokerTests : public Test { std::unique_ptr<FolderDataBroker> data_broker; void SetUp() override { data_broker = std::unique_ptr<FolderDataBroker> {new FolderDataBroker("/path/to/file")}; - data_broker->io__=std::unique_ptr<IO> {new FakeIO()}; + data_broker->io__ = std::unique_ptr<IO> {new FakeIO()}; } void TearDown() override { } @@ -82,7 +83,7 @@ TEST_F(FolderDataBrokerTests, CanConnect) { } TEST_F(FolderDataBrokerTests, CannotConnectWhenNoFolder) { - data_broker->io__=std::unique_ptr<IO> {new IOFolderNotFound()}; + data_broker->io__ = std::unique_ptr<IO> {new IOFolderNotFound()}; auto return_code = data_broker->Connect(); @@ -90,7 +91,7 @@ TEST_F(FolderDataBrokerTests, CannotConnectWhenNoFolder) { } TEST_F(FolderDataBrokerTests, ConnectReturnsUnknownIOError) { - data_broker->io__=std::unique_ptr<IO> {new IOFodlerUnknownError()}; + data_broker->io__ = std::unique_ptr<IO> {new IOFodlerUnknownError()}; auto return_code = data_broker->Connect();