diff --git a/.gitignore b/.gitignore index 8a3e47d4192b2b89bd8d537d36b37e4e61802b40..13bf1b11ad3dd89a04c62bc84790f85f08b1dc8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,3 @@ -#Astyle -.orig - - # Created by https://www.gitignore.io/api/c++,cmake,clion+all ### C++ ### @@ -113,6 +109,9 @@ build # End of https://www.gitignore.io/api/c++,cmake,clion+all +#Astyle +*.orig + ### Doxygen ### doxygen diff --git a/CMakeLists.txt b/CMakeLists.txt index cb5aa714b0903e185dab371f7eacfff2c4450411..d4e6c35a31a5615fd037d2fa899c98fdbf564709 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,20 @@ ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") ENDIF(WIN32) +#TODO: Better way then GLOBAL PROPERTY +IF(WIN32) + find_package(Threads REQUIRED) + SET_PROPERTY(GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} wsock32 ws2_32) +ELSEIF(UNIX) + SET_PROPERTY(GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES Threads::Threads) +ENDIF(WIN32) + option(BUILD_TESTS "Uses googletest to build tests" OFF) option(BUILD_INTEGRATION_TESTS "Include integration tests (CMAKE >3.7 is needed)" OFF) option(BUILD_DOCS "Uses doxygen to build the documentaion" OFF) +option(BUILD_BROKER "Build broker" OFF) +option(BUILD_WORKER_TOOLS "Build worker tools" OFF) +option(BUILD_EXAMPLES "Build examples" OFF) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules/) @@ -30,7 +41,7 @@ endif() add_subdirectory(common/cpp) -if (BUILD_BROKER) +if (BUILD_BROKER)#TODO: Somehow make it clear that this is needed by examples/worker/getnext_broker add_subdirectory(broker) endif() @@ -38,10 +49,7 @@ add_subdirectory(producer/api) add_subdirectory(worker) - -IF(UNIX) - add_subdirectory(receiver) -ENDIF(UNIX) +add_subdirectory(receiver) if(BUILD_INTEGRATION_TESTS) add_subdirectory(tests) diff --git a/CMakeModules/testing_cpp.cmake b/CMakeModules/testing_cpp.cmake index 5efbb83b05f1afd6480039b1d68d65fe60f6a383..1eb817ad1a5acee7ae379f6c1dbb558364dad235 100644 --- a/CMakeModules/testing_cpp.cmake +++ b/CMakeModules/testing_cpp.cmake @@ -18,6 +18,39 @@ if (BUILD_TESTS) endif () endif () +#TODO: Call add_plain_unit_test in gtest +function(add_plain_unit_test target test_source_files linktarget) + if (BUILD_TESTS) + include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) + link_directories(${gtest_SOURCE_DIR}/lib) + + add_executable(test-${target} ${test_source_files}) + + if (NOT ${libs} STREQUAL "") + target_link_libraries(test-${target} ${libs}) + endif () + + IF (WIN32 AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(GTEST_LIBS gtestd gtest_maind gmockd) + ELSE () + set(GTEST_LIBS gtest gmock gtest_main) + ENDIF (WIN32 AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") + target_link_libraries(test-${target} ${GTEST_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + + GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) + message(STATUS "HIDRA2_COMMON_IO_LIBRARIES: '${HIDRA2_COMMON_IO_LIBRARIES}'") + target_link_libraries(test-${target} ${HIDRA2_COMMON_IO_LIBRARIES}) + + if (NOT ${test_libraries} STREQUAL "") + target_link_libraries(test-${target} ${test_libraries}) + endif () + add_test(NAME test-${target} COMMAND test-${target}) + set_tests_properties(test-${target} PROPERTIES LABELS "unit;all") + + message(STATUS "Added test 'test-${target}'") + endif() +endfunction() + function(gtest target test_source_files linktarget) if (BUILD_TESTS) include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) @@ -48,6 +81,13 @@ function(gtest target test_source_files linktarget) ENDIF (WIN32 AND ${CMAKE_BUILD_TYPE} STREQUAL "Debug") target_link_libraries(test-${target} ${GTEST_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) + message(STATUS "HIDRA2_COMMON_IO_LIBRARIES: '${HIDRA2_COMMON_IO_LIBRARIES}'") + target_link_libraries(test-${target} ${HIDRA2_COMMON_IO_LIBRARIES}) + + if (NOT ${test_libraries} STREQUAL "") + target_link_libraries(test-${target} ${test_libraries}) + endif () add_test(NAME test-${target} COMMAND test-${target}) set_tests_properties(test-${target} PROPERTIES LABELS "unit;all") @@ -163,6 +203,11 @@ function(add_script_test testname arguments) separate_arguments(memargs) add_test(NAME memtest-${testname} COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/check_linux.sh ${memargs}) + set_tests_properties(memtest-${testname} PROPERTIES + LABELS "memcheck_${label};all" + DEPENDS test-${testname} + ) + endif () endif () ENDIF () diff --git a/CMakeModules/testing_go.cmake b/CMakeModules/testing_go.cmake index 3fcdb6e501075a3e234e2c95dab63b52be6fcd8a..56d7c168803185e6fd58055ddb028a264ab4d7cd 100644 --- a/CMakeModules/testing_go.cmake +++ b/CMakeModules/testing_go.cmake @@ -24,6 +24,7 @@ function(gotest target test_source_files) COMMAND ${CMAKE_MODULE_PATH}/coverage_go.sh ${CMAKE_CURRENT_BINARY_DIR} ${HIDRA2_MINIMUM_COVERAGE} ${gopath} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + set_tests_properties(coveragetest-${target} PROPERTIES LABELS "coverage;all") endif() endif () endfunction() diff --git a/README.md b/README.md index f527b949f40368117555570d4b81c18f31c01266..680a681206b2457a68668700aff3b29dae75f735 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# HIDRA2 +# hidra2 # C++ Projects @@ -8,7 +8,7 @@ - /producer/producer-api - **Library:** Producer library which can send data to the receiver + **Library:** ProducerImpl library which can send data to the receiver - /receiver diff --git a/common/cpp/CMakeLists.txt b/common/cpp/CMakeLists.txt index 0b489c5d47d4bc9e0ef3ff57d59b27f003dc972e..2e79b345e35a1a91c74e90320c463f8f66b3800d 100644 --- a/common/cpp/CMakeLists.txt +++ b/common/cpp/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(src/system_io) +add_subdirectory(src/common) add_subdirectory(src/json_parser) @@ -11,3 +12,4 @@ endif() install(DIRECTORY ${HIDRA2_CXX_COMMON_INCLUDE_DIR}/common DESTINATION ${CMAKE_INSTALL_PREFIX}/include ) + diff --git a/common/cpp/include/common/error.h b/common/cpp/include/common/error.h index 8ef74d4f5963df36225a46efa25515cdf870129f..e4602afd5a948194d8d7eb2214a6c40d69945c15 100644 --- a/common/cpp/include/common/error.h +++ b/common/cpp/include/common/error.h @@ -3,35 +3,87 @@ #include <string> #include <memory> +#include <utility> namespace hidra2 { enum class ErrorType { + kUnknownError, + kHidraError, - kEOF, - kHttpError + kHttpError, + kIOError, + kReceiverError, + kProducerError, + + kMemoryAllocationError, + kEndOfFile }; +class ErrorInterface; +class ErrorTemplateInterface; + +// nullptr == noError +// Example check: +// void TestError(Error* err) { +// if(*err) { +// [...] //An error occurred +// } +// } +using Error = std::unique_ptr<ErrorInterface>; + class ErrorInterface { public: virtual std::string Explain() const noexcept = 0; virtual void Append(const std::string& value) noexcept = 0; - virtual ErrorType GetErrorType() noexcept = 0; + virtual ErrorType GetErrorType() const noexcept = 0; virtual ~ErrorInterface() = default; // needed for unique_ptr to delete itself +}; + +class ErrorTemplateInterface { + public: + virtual ErrorType GetErrorType() const noexcept = 0; + virtual Error Generate() const noexcept = 0; + virtual std::string Text() const noexcept = 0; + + virtual inline bool operator == (const Error& rhs) const { + return rhs != nullptr && + GetErrorType() == rhs->GetErrorType(); + } + + virtual inline bool operator != (const Error& rhs) const { + return !(operator==(rhs)); + } }; -using Error = std::unique_ptr<ErrorInterface>; +static inline bool operator == (const Error& lhs, const ErrorTemplateInterface& rhs) { + return rhs.operator == (lhs); +} + +static inline bool operator != (const Error& lhs, const ErrorTemplateInterface& rhs) { + return rhs.operator != (lhs); +} + +static inline std::ostream& operator<<(std::ostream& os, const Error& err) { + if(err) { + os << err->Explain(); + } else { + static std::string no_error = "No error"; + os << no_error; + } + return os; +} class SimpleError: public ErrorInterface { private: std::string error_; ErrorType error_type_ = ErrorType::kHidraError; public: - explicit SimpleError(const std::string& error): error_{error} { + explicit SimpleError(std::string error): error_{std::move(error)} { } - SimpleError(const std::string& error, ErrorType error_type ): error_{error}, error_type_{error_type} { + SimpleError(std::string error, ErrorType error_type ): error_{std::move(error)}, error_type_{error_type} { } void Append(const std::string& value) noexcept override { @@ -41,12 +93,49 @@ class SimpleError: public ErrorInterface { std::string Explain() const noexcept override { return error_; } - ErrorType GetErrorType()noexcept override { + + ErrorType GetErrorType() const noexcept override { return error_type_; } +}; + +/* + * IMPORTANT: + * Never use the same ErrorType for two different errors, + * otherwise the == operator might not work as expected! + */ +class SimpleErrorTemplate : public ErrorTemplateInterface { + protected: + std::string error_; + ErrorType error_type_ = ErrorType::kHidraError; + public: + explicit SimpleErrorTemplate(std::string error): error_{std::move(error)} { + + } + + virtual std::string Text() const noexcept override { + return error_; + } + + + SimpleErrorTemplate(std::string error, ErrorType error_type ): error_{std::move(error)}, error_type_{error_type} { + } + + inline ErrorType GetErrorType() const noexcept override { + return error_type_; + } + + inline Error Generate() const noexcept override { + return Error(new SimpleError{error_, error_type_}); + } }; +static inline std::ostream& operator<<(std::ostream& os, const SimpleErrorTemplate& err) { + return os << err.Text(); +} + + inline Error TextError(const std::string& error) { return Error{new SimpleError{error}}; } @@ -55,6 +144,15 @@ inline Error TextErrorWithType(const std::string& error, ErrorType error_type) { return Error{new SimpleError{error, error_type}}; } +namespace ErrorTemplates { +auto const kMemoryAllocationError = SimpleErrorTemplate { + "kMemoryAllocationError", ErrorType::kMemoryAllocationError +}; +auto const kEndOfFile = SimpleErrorTemplate { + "End of file", ErrorType::kEndOfFile +}; + +} } #endif //HIDRA2_ERROR_H diff --git a/common/cpp/include/common/io_error.h b/common/cpp/include/common/io_error.h new file mode 100644 index 0000000000000000000000000000000000000000..40c9d4dd7e2c62ad73531d0a4aa18ebbbdeff2d0 --- /dev/null +++ b/common/cpp/include/common/io_error.h @@ -0,0 +1,142 @@ +#ifndef HIDRA2_SYSTEM__IO_ERROR_H +#define HIDRA2_SYSTEM__IO_ERROR_H + +#include "common/error.h" + +namespace hidra2 { + + +enum class IOErrorType { + kUnknownIOError, + kBadFileNumber, + kResourceTemporarilyUnavailable, + kFileNotFound, + kReadError, + kPermissionDenied, + kUnsupportedAddressFamily, + kInvalidAddressFormat, + kAddressAlreadyInUse, + kConnectionRefused, + kConnectionResetByPeer, + kTimeout, + kFileAlreadyExists, + kNoSpaceLeft, + kSocketOperationOnNonSocket, + kInvalidMemoryAddress, + kUnableToResolveHostname, + kSocketOperationUnknownAtLevel, + kSocketOperationValueOutOfBound, + kAddressNotValid + +}; + +class IOError : public SimpleError { + private: + IOErrorType io_error_type_; + public: + IOError(const std::string& error, IOErrorType io_error_type) : SimpleError(error, ErrorType::kIOError) { + io_error_type_ = io_error_type; + } + + IOErrorType GetIOErrorType() const noexcept { + return io_error_type_; + } +}; + +class IOErrorTemplate : public SimpleErrorTemplate { + protected: + IOErrorType io_error_type_; + public: + IOErrorTemplate(const std::string& error, IOErrorType io_error_type) : SimpleErrorTemplate(error, ErrorType::kIOError) { + io_error_type_ = io_error_type; + } + + inline IOErrorType GetIOErrorType() const noexcept { + return io_error_type_; + } + + inline Error Generate() const noexcept override { + return Error(new IOError(error_, io_error_type_)); + } + + inline bool operator == (const Error& rhs) const override { + return SimpleErrorTemplate::operator==(rhs) + && GetIOErrorType() == ((IOError*)rhs.get())->GetIOErrorType(); + } +}; + +static inline std::ostream& operator<<(std::ostream& os, const IOErrorTemplate& err) { + return os << err.Text(); +} + + +namespace IOErrorTemplates { +auto const kUnknownIOError = IOErrorTemplate { + "Unknown Error", IOErrorType::kUnknownIOError +}; + +auto const kFileNotFound = IOErrorTemplate { + "No such file or directory", IOErrorType::kFileNotFound +}; +auto const kReadError = IOErrorTemplate { + "Read error", IOErrorType::kReadError +}; +auto const kBadFileNumber = IOErrorTemplate { + "Bad file number", IOErrorType::kBadFileNumber +}; +auto const kResourceTemporarilyUnavailable = IOErrorTemplate { + "Resource temporarily unavailable", IOErrorType::kResourceTemporarilyUnavailable +}; +auto const kPermissionDenied = IOErrorTemplate { + "Permission denied", IOErrorType::kPermissionDenied +}; +auto const kUnsupportedAddressFamily = IOErrorTemplate { + "Unsupported address family", IOErrorType::kUnsupportedAddressFamily +}; +auto const kInvalidAddressFormat = IOErrorTemplate { + "Invalid address format", IOErrorType::kInvalidAddressFormat +}; +auto const kAddressAlreadyInUse = IOErrorTemplate { + "Address already in use", IOErrorType::kAddressAlreadyInUse +}; +auto const kConnectionRefused = IOErrorTemplate { + "Connection refused", IOErrorType::kConnectionRefused +}; +auto const kConnectionResetByPeer = IOErrorTemplate { + "kConnectionResetByPeer", IOErrorType::kConnectionResetByPeer +}; +auto const kTimeout = IOErrorTemplate { + "kTimeout", IOErrorType::kTimeout +}; +auto const kFileAlreadyExists = IOErrorTemplate { + "kFileAlreadyExists", IOErrorType::kFileAlreadyExists +}; +auto const kNoSpaceLeft = IOErrorTemplate { + "kNoSpaceLeft", IOErrorType::kNoSpaceLeft +}; +auto const kSocketOperationOnNonSocket = IOErrorTemplate { + "kSocketOperationOnNonSocket", IOErrorType::kSocketOperationOnNonSocket +}; +auto const kInvalidMemoryAddress = IOErrorTemplate { + "kInvalidMemoryAddress", IOErrorType::kInvalidMemoryAddress +}; +auto const kUnableToResolveHostname = IOErrorTemplate { + "kUnableToResolveHostname", IOErrorType::kUnableToResolveHostname +}; +auto const kSocketOperationUnknownAtLevel = IOErrorTemplate { + "kSocketOperationUnknownAtLevel", IOErrorType::kSocketOperationUnknownAtLevel +}; + +auto const kSocketOperationValueOutOfBound = IOErrorTemplate { + "kSocketOperationValueOutOfBound", IOErrorType::kSocketOperationValueOutOfBound +}; + +auto const kAddressNotValid = IOErrorTemplate { + "Address not valid", IOErrorType::kAddressNotValid +}; + +} + +} + +#endif //HIDRA2_SYSTEM__IO_ERROR_H diff --git a/common/cpp/include/common/networking.h b/common/cpp/include/common/networking.h index b3cd443c0335c619403043e60610ab4a7734d404..1e2c806e4072449e4c3a42503ad6cf4824077cc8 100644 --- a/common/cpp/include/common/networking.h +++ b/common/cpp/include/common/networking.h @@ -1,43 +1,54 @@ -#ifndef HIDRA2__COMMON_NETWORKING_H -#define HIDRA2__COMMON_NETWORKING_H +#ifndef HIDRA2_COMMON__NETWORKING_H +#define HIDRA2_COMMON__NETWORKING_H #include <cstdint> -#include "os.h" namespace hidra2 { -enum OP_CODE : uint8_t { - OP_CODE__HELLO, -}; -enum ERROR_CODE : uint16_t { - ERR__NO_ERROR, - ERR__UNSUPPORTED_VERSION, - ERR__INTERNAL_SERVER_ERROR = 65535, -}; +typedef uint64_t NetworkRequestId; -struct NetworkRequest { - OP_CODE op_code; - uint64_t request_id; - char data[]; +enum Opcode : uint8_t { + kNetOpcodeUnknownOp, + kNetOpcodeSendData, + kNetOpcodeCount, }; -struct NetworkResponse { - OP_CODE op_code; - uint64_t request_id; - ERROR_CODE error_code; - char data[]; +enum NetworkErrorCode : uint16_t { + kNetErrorNoError, + kNetErrorFileIdAlreadyInUse, + kNetErrorAllocateStorageFailed, + kNetErrorInternalServerError = 65535, }; -struct OP_HelloRequest { - uint32_t client_version; +//TODO need to use an serialization framework to ensure struct consistency on different computers + +/** + * @defgroup RPC + * RPC always return a response to a corresponding request + * @{ + */ +struct GenericNetworkRequestHeader { + Opcode op_code; + NetworkRequestId request_id; + uint64_t data_id; + uint64_t data_size; +}; - OS_TYPE os : 4; - bool is_x64 : 1; +struct GenericNetworkResponse { + Opcode op_code; + NetworkRequestId request_id; + NetworkErrorCode error_code; }; -struct OP_HelloResponse { - uint32_t server_version; +/** + * Possible error codes: + * - ::NET_ERR__FILENAME_ALREADY_IN_USE + * - ::NET_ERR__ALLOCATE_STORAGE_FAILED + */ +struct SendDataResponse : GenericNetworkResponse { }; +/** @} */ + } -#endif //HIDRA2__COMMON_NETWORKING_H +#endif //HIDRA2_COMMON__NETWORKING_H diff --git a/common/cpp/include/common/os.h b/common/cpp/include/common/os.h deleted file mode 100644 index ddd8c2a3e814b87031b4b48cefbf8f2e2b64de96..0000000000000000000000000000000000000000 --- a/common/cpp/include/common/os.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HIDRA2__COMMON_OS_H -#define HIDRA2__COMMON_OS_H - -#include <cstdint> - -namespace hidra2 { -enum OS_TYPE : uint8_t { - OS_UNKOWN, - OS_LINUX, - OS_WINDOWS, - - OS_INVALID = 16, /* Never use more then 4 bit */ -}; -} - -#endif //HIDRA2__COMMON_OS_H diff --git a/common/cpp/include/io/io.h b/common/cpp/include/io/io.h new file mode 100644 index 0000000000000000000000000000000000000000..61e03685cff91788b68b8225e2e7ff7ab33a6004 --- /dev/null +++ b/common/cpp/include/io/io.h @@ -0,0 +1,106 @@ +#ifndef HIDRA2_SYSTEM__IO_H +#define HIDRA2_SYSTEM__IO_H + +#include <cinttypes> + +#include <string> +#include <vector> +#include <chrono> +#include <thread> + +#include "common/data_structs.h" +#include "common/io_error.h" + +namespace hidra2 { + +//Need to be "enum" since multiple flags are allowed +enum FileOpenMode { + IO_OPEN_MODE_READ = 1 << 0, + IO_OPEN_MODE_WRITE = 1 << 1, + IO_OPEN_MODE_RW = IO_OPEN_MODE_READ | IO_OPEN_MODE_WRITE, + IO_OPEN_MODE_CREATE = 1 << 2, + IO_OPEN_MODE_CREATE_AND_FAIL_IF_EXISTS = 1 << 3, + /** + * Will set the length of a file to 0 + * Only works if file is open with READ and WRITE mode + */ + IO_OPEN_MODE_SET_LENGTH_0 = 1 << 4, +}; + +enum class AddressFamilies { + INET, +}; + +enum class SocketTypes { + STREAM, +}; + +enum class SocketProtocols { + IP, +}; + +using FileDescriptor = int; +using SocketDescriptor = int; + +class IO { + public: + + /* + * Special + */ + virtual std::unique_ptr<std::thread> NewThread (std::function<void()> function) const = 0; + + /* + * Network + */ + virtual SocketDescriptor CreateSocket(AddressFamilies address_family, SocketTypes socket_type, + SocketProtocols socket_protocol, Error* err) const = 0; + virtual void Listen(SocketDescriptor socket_fd, int backlog, Error* err) const = 0; + virtual void InetBind(SocketDescriptor socket_fd, const std::string& address, Error* err) const = 0; + virtual SocketDescriptor CreateAndBindIPTCPSocketListener(const std::string& address, int backlog, + Error* err) const = 0; + virtual std::unique_ptr<std::tuple<std::string, SocketDescriptor>> InetAcceptConnection(SocketDescriptor socket_fd, + Error* err) const = 0; + virtual std::string ResolveHostnameToIp(const std::string& hostname, Error* err) const = 0; + virtual void InetConnect(SocketDescriptor socket_fd, const std::string& address, Error* err) const = 0; + virtual SocketDescriptor CreateAndConnectIPTCPSocket(const std::string& address, Error* err) const = 0; + virtual size_t Receive(SocketDescriptor socket_fd, void* buf, size_t length, Error* err) const = 0; + virtual size_t ReceiveWithTimeout(SocketDescriptor socket_fd, + void* buf, + size_t length, + long timeout_in_usec, + Error* err) const = 0; + virtual size_t Send(SocketDescriptor socket_fd, const void* buf, size_t length, Error* err) const = 0; + + virtual void Skip(SocketDescriptor socket_fd, size_t length, Error* err) const = 0; + /** + * @param err Since CloseSocket if often used in an error case, it's able to accept err as nullptr. + */ + virtual void CloseSocket(SocketDescriptor socket_fd, Error* err) const = 0; + + /* + * Filesystem + */ + virtual FileDescriptor Open (const std::string& filename, int open_flags, Error* err) const = 0; + /** + * @param err Since Close if often used in an error case, it's able to accept err as nullptr. + */ + virtual void Close (FileDescriptor fd, Error* err) const = 0; + + virtual size_t Read (FileDescriptor fd, void* buf, size_t length, Error* err) const = 0; + virtual size_t Write (FileDescriptor fd, const void* buf, size_t length, Error* err) const = 0; + + virtual Error WriteDataToFile (const std::string& fname, const FileData& data, size_t length) const = 0; + + virtual void CreateNewDirectory (const std::string& directory_name, Error* err) const = 0; + virtual FileData GetDataFromFile (const std::string& fname, uint64_t fsize, Error* err) const = 0; + virtual void CollectFileInformationRecursively(const std::string& path, std::vector<FileInfo>* files, + Error* err) const = 0; + virtual std::vector<FileInfo> FilesInFolder (const std::string& folder, Error* err) const = 0; + virtual std::string ReadFileToString (const std::string& fname, Error* err) const = 0; + +}; + +} + +#endif //HIDRA2_SYSTEM__IO_H diff --git a/common/cpp/include/io/io_factory.h b/common/cpp/include/io/io_factory.h new file mode 100644 index 0000000000000000000000000000000000000000..4dd614beecaa05e1fcbf51cfbdbabfb6f881e495 --- /dev/null +++ b/common/cpp/include/io/io_factory.h @@ -0,0 +1,12 @@ +#ifndef HIDRA2_IO_FACTORY_H +#define HIDRA2_IO_FACTORY_H + +#include "io.h" + +namespace hidra2 { + +IO* GenerateDefaultIO(); + +} + +#endif //HIDRA2_IO_FACTORY_H \ No newline at end of file diff --git a/common/cpp/include/system_wrappers/io.h b/common/cpp/include/system_wrappers/io.h deleted file mode 100644 index 5b0f4739903dbb21d0bf86d9d4f6ef6d59b5427e..0000000000000000000000000000000000000000 --- a/common/cpp/include/system_wrappers/io.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef HIDRA2_SYSTEM_WRAPPERS__IO_H -#define HIDRA2_SYSTEM_WRAPPERS__IO_H - -#include <cinttypes> - -#include <string> -#include <vector> -#include <chrono> - -#include "common/data_structs.h" -#include "common/error.h" - -namespace hidra2 { - -namespace IOErrors { -auto const kFileNotFound = "No such file or directory"; -auto const kReadError = "Read error"; -auto const kPermissionDenied = "Permission denied"; -auto const kUnknownError = "Unknown error"; -auto const kMemoryAllocationError = "Memory Allocation Error"; -} - -Error IOErrorFromErrno(); - - -class IO { - public: - - virtual FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept = 0; - virtual uint64_t Read(int fd, uint8_t* array, uint64_t fsize, Error* err) const noexcept = 0; - virtual std::string ReadFileToString(const std::string& fname, Error* err)const noexcept = 0; - virtual int open(const char* __file, int __oflag) const noexcept = 0; - virtual int close(int __fd) const noexcept = 0; - -// this is not standard function - to be implemented differently in windows and linux - virtual FileInfos FilesInFolder(const std::string& folder, Error* err) const = 0; -}; - -} - -#endif //HIDRA2_SYSTEM_WRAPPERS__IO_H diff --git a/common/cpp/include/system_wrappers/system_io.h b/common/cpp/include/system_wrappers/system_io.h deleted file mode 100644 index baf715b12b7650ccf7915ec2e0fe21c76d6c43d9..0000000000000000000000000000000000000000 --- a/common/cpp/include/system_wrappers/system_io.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef HIDRA2_SYSTEM_WRAPPERS__SYSTEM_IO_H -#define HIDRA2_SYSTEM_WRAPPERS__SYSTEM_IO_H - -#include "io.h" - -namespace hidra2 { - -class SystemIO final : public IO { - public: - FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept override; - std::string ReadFileToString(const std::string& fname, Error* err) const noexcept override; - int open(const char* __file, int __oflag) const noexcept override; - int close(int __fd) const noexcept override; - uint64_t Read(int fd, uint8_t* array, uint64_t fsize, Error* err) const noexcept override; - std::vector<FileInfo> FilesInFolder(const std::string& folder, Error* err) const override; - private: - void CollectFileInformationRecursivly(const std::string& path, - std::vector<FileInfo>* files, Error* err) const; - int64_t read(int __fd, void* buf, size_t count) const noexcept; - int64_t write(int __fd, const void* __buf, size_t __n) const noexcept; - int OpenFile(const std::string& fname, Error* err) const noexcept; - -}; - -FileInfo GetFileInfo(const std::string& name, Error* err); - -} - -#endif //HIDRA2_SYSTEM_WRAPPERS__SYSTEM_IO_H diff --git a/common/cpp/include/unittests/MockIO.h b/common/cpp/include/unittests/MockIO.h index 5a9ca436b1279a58da045ccecf7416f282708bbd..5da606c100fc57e9bc9c23ff310b09f52d71ef90 100644 --- a/common/cpp/include/unittests/MockIO.h +++ b/common/cpp/include/unittests/MockIO.h @@ -4,69 +4,208 @@ #include <gtest/gtest.h> #include <gmock/gmock.h> -#include "system_wrappers/io.h" - +#include "io/io.h" namespace hidra2 { - class MockIO : public IO { public: - std::string ReadFileToString(const std::string& fname, Error* err)const noexcept override { - SimpleError* error; - auto data = ReadFileToString_t(fname, &error); + std::unique_ptr<std::thread> NewThread(std::function<void()> function) const override { + return std::unique_ptr<std::thread>(NewThread_t(function)); + } + MOCK_CONST_METHOD1(NewThread_t, std::thread * (std::function<void()> function)); + + SocketDescriptor CreateSocket(AddressFamilies address_family, SocketTypes socket_type, SocketProtocols socket_protocol, + Error* err) const override { + ErrorInterface* error = nullptr; + auto data = CreateSocket_t(address_family, socket_type, socket_protocol, &error); err->reset(error); return data; } + MOCK_CONST_METHOD4(CreateSocket_t, SocketDescriptor(AddressFamilies address_family, SocketTypes socket_type, + SocketProtocols socket_protocol, ErrorInterface** err)); - FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept override { - SimpleError* error; - auto data = GetDataFromFile_t(fname, fsize, &error); + void Listen(SocketDescriptor socket_fd, int backlog, Error* err) const override { + ErrorInterface* error = nullptr; + Listen_t(socket_fd, backlog, &error); err->reset(error); - return FileData(data); } - int open(const char* __file, int __oflag) const noexcept override { - return 0; + MOCK_CONST_METHOD3(Listen_t, void(SocketDescriptor socket_fd, int backlog, ErrorInterface** err)); + + + void InetBind(SocketDescriptor socket_fd, const std::string& address, Error* err) const override { + ErrorInterface* error = nullptr; + InetBind_t(socket_fd, address, &error); + err->reset(error); } - int close(int __fd) const noexcept override { - return 0; + MOCK_CONST_METHOD3(InetBind_t, void(SocketDescriptor socket_fd, const std::string& address, ErrorInterface** err)); + + SocketDescriptor CreateAndBindIPTCPSocketListener(const std::string& address, int backlog, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = CreateAndBindIPTCPSocketListener_t(address, backlog, &error); + err->reset(error); + return data; } - uint64_t Read(int fd, uint8_t* array, uint64_t fsize, Error* err) const noexcept override { - return 0; + MOCK_CONST_METHOD3(CreateAndBindIPTCPSocketListener_t, SocketDescriptor(const std::string& address, int backlog, + ErrorInterface** err)); + + + std::unique_ptr<std::tuple<std::string, SocketDescriptor>> InetAcceptConnection(SocketDescriptor socket_fd, + Error* err) const override { + ErrorInterface* error = nullptr; + auto data = InetAcceptConnection_t(socket_fd, &error); + err->reset(error); + return std::unique_ptr<std::tuple<std::string, SocketDescriptor>>(data); } + MOCK_CONST_METHOD2(InetAcceptConnection_t, std::tuple<std::string, SocketDescriptor>* (SocketDescriptor socket_fd, + ErrorInterface** err)); - FileInfos FilesInFolder(const std::string& folder, Error* err) const override { - SimpleError* error; - auto data = FilesInFolder_t(folder, &error); + std::string ResolveHostnameToIp(const std::string& hostname, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = ResolveHostnameToIp_t(hostname, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD2(ResolveHostnameToIp_t, std::string(const std::string& hostname, ErrorInterface** err)); + + void InetConnect(SocketDescriptor socket_fd, const std::string& address, Error* err) const override { + ErrorInterface* error = nullptr; + InetConnect_t(socket_fd, address, &error); + err->reset(error); + } + MOCK_CONST_METHOD3(InetConnect_t, void(SocketDescriptor socket_fd, const std::string& address, ErrorInterface** err)); + + SocketDescriptor CreateAndConnectIPTCPSocket(const std::string& address, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = CreateAndConnectIPTCPSocket_t(address, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD2(CreateAndConnectIPTCPSocket_t, SocketDescriptor(const std::string& address, ErrorInterface** err)); + + size_t Receive(SocketDescriptor socket_fd, void* buf, size_t length, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = Receive_t(socket_fd, buf, length, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD4(Receive_t, size_t(SocketDescriptor socket_fd, void* buf, size_t length, ErrorInterface** err)); + + size_t ReceiveWithTimeout(SocketDescriptor socket_fd, void* buf, size_t length, long timeout_in_usec, + Error* err) const override { + ErrorInterface* error = nullptr; + auto data = ReceiveWithTimeout_t(socket_fd, buf, length, timeout_in_usec, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD5(ReceiveWithTimeout_t, size_t(SocketDescriptor socket_fd, void* buf, size_t length, + long timeout_in_usec, + ErrorInterface** err)); + + size_t Send(SocketDescriptor socket_fd, const void* buf, size_t length, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = Send_t(socket_fd, buf, length, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD4(Send_t, size_t(SocketDescriptor socket_fd, const void* buf, size_t length, ErrorInterface** err)); + + void Skip(SocketDescriptor socket_fd, size_t length, Error* err) const override { + ErrorInterface* error = nullptr; + Skip_t(socket_fd, length, &error); + err->reset(error); + } + MOCK_CONST_METHOD3(Skip_t, void(SocketDescriptor socket_fd, size_t length, ErrorInterface** err)); + + void CloseSocket(SocketDescriptor socket_fd, Error* err) const override { + ErrorInterface* error = nullptr; + CloseSocket_t(socket_fd, &error); + if(err) { + err->reset(error); + } + } + MOCK_CONST_METHOD2(CloseSocket_t, void(SocketDescriptor socket_fd, ErrorInterface** err)); + + FileDescriptor Open(const std::string& filename, int open_flags, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = Open_t(filename, open_flags, &error); err->reset(error); return data; } - MOCK_CONST_METHOD2(ReadFileToString_t, - std::string (const std::string& fname, SimpleError** err)); + MOCK_CONST_METHOD3(Open_t, FileDescriptor(const std::string& filename, int open_flags, ErrorInterface** err)); - MOCK_CONST_METHOD3(GetDataFromFile_t, - uint8_t* (const std::string& fname, uint64_t fsize, SimpleError** err)); - MOCK_CONST_METHOD2(FilesInFolder_t, - FileInfos( - const std::string& folder, hidra2::SimpleError - ** err)); + void Close(FileDescriptor fd, Error* err) const override { + ErrorInterface* error = nullptr; + Close_t(fd, &error); + if(err) { + err->reset(error); + }; + } + MOCK_CONST_METHOD2(Close_t, void(FileDescriptor fd, ErrorInterface** err)); - MOCK_CONST_METHOD3(read_t, - int64_t(int - __fd, void* buf, size_t - count)); + size_t Read(FileDescriptor fd, void* buf, size_t length, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = Read_t(fd, buf, length, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD4(Read_t, size_t(FileDescriptor fd, void* buf, size_t length, ErrorInterface** err)); - MOCK_CONST_METHOD3(write_t, - int64_t(int - __fd, - const void* __buf, size_t - __n)); + size_t Write(FileDescriptor fd, const void* buf, size_t length, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = Write_t(fd, buf, length, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD4(Write_t, size_t(FileDescriptor fd, const void* buf, size_t length, ErrorInterface** err)); - MOCK_CONST_METHOD2(open_t, - int(const char* __file, int __oflag)); + void CreateNewDirectory(const std::string& directory_name, hidra2::Error* err) const override { + ErrorInterface* error = nullptr; + CreateNewDirectory_t(directory_name, &error); + err->reset(error); + } + MOCK_CONST_METHOD2(CreateNewDirectory_t, void(const std::string& directory_name, ErrorInterface** err)); - MOCK_CONST_METHOD1(close_t, - int(int __fd)); + FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = GetDataFromFile_t(fname, fsize, &error); + err->reset(error); + return FileData(data); + } + + MOCK_CONST_METHOD3(GetDataFromFile_t, uint8_t* (const std::string& fname, uint64_t fsize, ErrorInterface** err)); + + Error WriteDataToFile(const std::string& fname, const FileData& data, size_t length) const override { + return Error{WriteDataToFile_t(fname, data.get(), length)}; + + } + + MOCK_CONST_METHOD3(WriteDataToFile_t, ErrorInterface * (const std::string& fname, uint8_t* data, size_t fsize)); + + void CollectFileInformationRecursively(const std::string& path, std::vector<FileInfo>* files, + Error* err) const override { + ErrorInterface* error = nullptr; + CollectFileInformationRecursivly_t(path, files, &error); + err->reset(error); + } + MOCK_CONST_METHOD3(CollectFileInformationRecursivly_t, void(const std::string& path, std::vector<FileInfo>* files, + ErrorInterface** err)); + + std::vector<FileInfo> FilesInFolder(const std::string& folder, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = FilesInFolder_t(folder, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD2(FilesInFolder_t, std::vector<FileInfo>(const std::string& folder, ErrorInterface** err)); + + std::string ReadFileToString(const std::string& fname, Error* err) const override { + ErrorInterface* error = nullptr; + auto data = ReadFileToString_t(fname, &error); + err->reset(error); + return data; + } + MOCK_CONST_METHOD2(ReadFileToString_t, std::string(const std::string& fname, ErrorInterface** err)); }; } -#endif //HIDRA2_COMMON__MOCKIO_H \ No newline at end of file +#endif //HIDRA2_COMMON__MOCKIO_H diff --git a/common/cpp/src/common/CMakeLists.txt b/common/cpp/src/common/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..40873cfcf7dba8ba3fd88b3246a20a05687b2fcb --- /dev/null +++ b/common/cpp/src/common/CMakeLists.txt @@ -0,0 +1,7 @@ +set(TARGET_NAME common) + +set(TEST_SOURCE_FILES ../../unittests/common/test_error.cpp) + +set(TEST_LIBRARIES "${TARGET_NAME};system_io") +include_directories(${HIDRA2_CXX_COMMON_INCLUDE_DIR}) +add_plain_unit_test(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}") diff --git a/common/cpp/src/json_parser/rapid_json.cpp b/common/cpp/src/json_parser/rapid_json.cpp index e3f1f6ee55a9839476b3fe9eb2eb014fe61b137f..37d1c8771aa04d6f4150df97c229157ce6c10916 100644 --- a/common/cpp/src/json_parser/rapid_json.cpp +++ b/common/cpp/src/json_parser/rapid_json.cpp @@ -3,11 +3,12 @@ using namespace rapidjson; -#include "system_wrappers/system_io.h" +#include "io/io_factory.h" namespace hidra2 { -RapidJson::RapidJson(const std::string& json, bool read_from_file): io__{new SystemIO}, json_{json}, read_from_file_{read_from_file} { +RapidJson::RapidJson(const std::string& json, bool read_from_file): io__{GenerateDefaultIO()}, json_{json}, + read_from_file_{read_from_file} { } diff --git a/common/cpp/src/json_parser/rapid_json.h b/common/cpp/src/json_parser/rapid_json.h index 6f55934c2a159eb8746a1132669e6f58c0b31e70..ef26611e4abcafc3137fd18e7eef18ab07a717ee 100644 --- a/common/cpp/src/json_parser/rapid_json.h +++ b/common/cpp/src/json_parser/rapid_json.h @@ -3,7 +3,7 @@ #include "rapidjson/document.h" #include "common/error.h" -#include "system_wrappers/io.h" +#include "io/io.h" namespace hidra2 { diff --git a/common/cpp/src/system_io/CMakeLists.txt b/common/cpp/src/system_io/CMakeLists.txt index 627606d1a12053ea62b8d9dacdc344ff06faa53d..274e3b7f7a9e17b3fe0c738d825b7ca7b1e44994 100644 --- a/common/cpp/src/system_io/CMakeLists.txt +++ b/common/cpp/src/system_io/CMakeLists.txt @@ -1,5 +1,6 @@ set(TARGET_NAME system_io) set(SOURCE_FILES + io_factory.cpp system_io.cpp) IF(WIN32) set(SOURCE_FILES ${SOURCE_FILES} system_io_windows.cpp) diff --git a/common/cpp/src/system_io/io_factory.cpp b/common/cpp/src/system_io/io_factory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de5e48a5d0a2e5dc345f47974e7a96adc36a8903 --- /dev/null +++ b/common/cpp/src/system_io/io_factory.cpp @@ -0,0 +1,13 @@ +#include "io/io_factory.h" + +#include "system_io.h" + +namespace hidra2 { + +IO* GenerateDefaultIO() { + return new SystemIO; +} + + +} + diff --git a/common/cpp/src/system_io/system_io.cpp b/common/cpp/src/system_io/system_io.cpp index ef64b4b0a9eed38943a9f0321a2a5839552c3ddf..70c4a93ddfbbe4fd6c6872491370d2539e651ff5 100644 --- a/common/cpp/src/system_io/system_io.cpp +++ b/common/cpp/src/system_io/system_io.cpp @@ -6,53 +6,64 @@ #include <cstring> #include <algorithm> -#include <system_wrappers/system_io.h> + +#if defined(__linux__) || defined (__APPLE__) +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +#ifdef __APPLE__ +#include <sys/select.h> +#endif + +#include "system_io.h" namespace hidra2 { -Error IOErrorFromErrno() { - const char* message; - switch (errno) { - case 0: - return nullptr; - case ENOENT: - case ENOTDIR: - message = IOErrors::kFileNotFound; - break; - case EACCES: - message = IOErrors::kPermissionDenied; - break; - default: - message = IOErrors::kUnknownError; - break; - } - return TextError(message); +const int SystemIO::kNetBufferSize = 1024 * 1024 ; //MiByte + +/******************************************************************************* + * system_io.cpp * + * THIS FILE HOLDS GENERAL FUNCTIONS THAT CAN BE USED ON WINDOWS AND ON LINUX * + *******************************************************************************/ + +// PRIVATE FUNCTIONS - START + +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; + }); } -uint64_t SystemIO::Read(int fd, uint8_t* array, uint64_t fsize, Error* err) const noexcept { - uint64_t totalbytes = 0; - int64_t readbytes = 0; - do { - readbytes = read(fd, array + totalbytes, fsize); - totalbytes += readbytes; - } while (readbytes > 0 && totalbytes < fsize); +void StripBasePath(const std::string& folder, std::vector<FileInfo>* file_list) { + auto n_erase = folder.size() + 1; + for (auto& file : *file_list) { + file.name.erase(0, n_erase); + } +} - if (totalbytes != fsize) { - *err = TextError(IOErrors::kReadError); +void AssignIDs(FileInfos* file_list) { + int64_t id = 0; + for (auto& file : *file_list) { + file.id = ++id; } - return totalbytes; } -int SystemIO::OpenFile(const std::string& fname, Error* err) const noexcept { - errno = 0; - int fd = open(fname.c_str(), O_RDONLY); - *err = IOErrorFromErrno(); - if (*err != nullptr) { - (*err)->Append(fname); - return 0; +std::unique_ptr<std::tuple<std::string, uint16_t>> SystemIO::SplitAddressToHostnameAndPort(std::string address) const { + try { + std::string host = address.substr(0, address.find(':')); + + std::string port_str = address.substr(address.find(':') + 1, address.length()); + uint16_t port = static_cast<uint16_t>(std::stoi(port_str)); + + return std::unique_ptr<std::tuple<std::string, uint16_t>>(new std::tuple<std::string, uint16_t>(host, port)); + } catch (...) { + return nullptr; } - return fd; } uint8_t* AllocateArray(uint64_t fsize, Error* err) { @@ -60,14 +71,17 @@ uint8_t* AllocateArray(uint64_t fsize, Error* err) { try { data_array = new uint8_t[fsize]; } catch (...) { - *err = TextError(IOErrors::kMemoryAllocationError); + *err = ErrorTemplates::kMemoryAllocationError.Generate(); return nullptr; } return data_array; } -FileData SystemIO::GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept { - auto fd = OpenFile(fname, err); +// PRIVATE FUNCTIONS - END + +FileData SystemIO::GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const { + *err = nullptr; + auto fd = Open(fname, IO_OPEN_MODE_READ, err); if (*err != nullptr) { return nullptr; } @@ -79,40 +93,18 @@ FileData SystemIO::GetDataFromFile(const std::string& fname, uint64_t fsize, Err Read(fd, data_array, fsize, err); if (*err != nullptr) { - close(fd); (*err)->Append(fname); + Close(fd, nullptr); return nullptr; } - close(fd); + Close(fd, err); return FileData{data_array}; } -void SortFileList(FileInfos* 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 std::string& folder, FileInfos* file_list) { - auto n_erase = folder.size() + 1; - for (auto& file : *file_list) { - file.name.erase(0, n_erase); - } -} - -void AssignIDs(FileInfos* file_list) { - int64_t id = 0; - for (auto& file : *file_list) { - file.id = ++id; - } -} - - FileInfos SystemIO::FilesInFolder(const std::string& folder, Error* err) const { FileInfos files{}; - CollectFileInformationRecursivly(folder, &files, err); + CollectFileInformationRecursively(folder, &files, err); if (*err != nullptr) { return {}; } @@ -122,8 +114,33 @@ FileInfos SystemIO::FilesInFolder(const std::string& folder, Error* err) const { return files; } -std::string SystemIO::ReadFileToString(const std::string& fname, Error* err)const noexcept { - auto info = GetFileInfo(fname, err); +void hidra2::SystemIO::CreateNewDirectory(const std::string& directory_name, Error* err) const { + if(_mkdir(directory_name.c_str()) == -1) { + *err = GetLastError(); + } else { + *err = nullptr; + } +} + +Error SystemIO::WriteDataToFile(const std::string& fname, const FileData& data, size_t length) const { + Error err; + auto fd = Open(fname, IO_OPEN_MODE_CREATE_AND_FAIL_IF_EXISTS | IO_OPEN_MODE_RW, &err); + if (err) { + return err; + } + + Write(fd, data.get(), length, &err); + if (err) { + return err; + } + + Close(fd, &err); + return err; +} + + +std::string SystemIO::ReadFileToString(const std::string& fname, Error* err) const { + auto info = GetFileInfo(fname, err); if (*err != nullptr) { return ""; } @@ -137,5 +154,389 @@ std::string SystemIO::ReadFileToString(const std::string& fname, Error* err)cons } +std::unique_ptr<std::thread> SystemIO::NewThread(std::function<void()> function) const { + return std::unique_ptr<std::thread>(new std::thread(function)); +} + +void SystemIO::Skip(SocketDescriptor socket_fd, size_t length, Error* err) const { + static const size_t kSkipBufferSize = 1024; + + //TODO need to find a better way to skip bytes + *err = nullptr; + std::unique_ptr<uint8_t[]> buffer; + try { + buffer.reset(new uint8_t[kSkipBufferSize]); + } catch(...) { + *err = ErrorTemplates::kMemoryAllocationError.Generate(); + return; + } + size_t already_skipped = 0; + while(already_skipped < length) { + size_t need_to_skip = length - already_skipped; + if(need_to_skip > kSkipBufferSize) + need_to_skip = kSkipBufferSize; + size_t skipped_amount = Receive(socket_fd, buffer.get(), need_to_skip, err); + if(*err != nullptr) { + return; + } + already_skipped += skipped_amount; + } +} + +hidra2::FileDescriptor hidra2::SystemIO::CreateAndConnectIPTCPSocket(const std::string& address, + Error* err) const { + *err = nullptr; + + FileDescriptor fd = CreateSocket(AddressFamilies::INET, SocketTypes::STREAM, SocketProtocols::IP, err); + if(*err != nullptr) { + return -1; + } + + InetConnect(fd, address, err); + if (*err != nullptr) { + CloseSocket(fd, nullptr); + return -1; + } + + return fd; +} + +int SystemIO::FileOpenModeToPosixFileOpenMode(int open_flags) const { + int flags = 0; + if((open_flags & IO_OPEN_MODE_READ && open_flags & IO_OPEN_MODE_WRITE) || open_flags & IO_OPEN_MODE_RW) { + flags |= O_RDWR; + } else { + if (open_flags & IO_OPEN_MODE_READ) { + flags |= O_RDONLY; + } + if (open_flags & IO_OPEN_MODE_WRITE) { + flags |= O_WRONLY; + } + } + if(open_flags & IO_OPEN_MODE_CREATE) { + flags |= O_CREAT; + } + if(open_flags & IO_OPEN_MODE_CREATE_AND_FAIL_IF_EXISTS) { + flags |= O_CREAT | O_EXCL; + } + if(open_flags & IO_OPEN_MODE_SET_LENGTH_0) { + flags |= O_TRUNC; + } + return flags; +} + +std::string SystemIO::ResolveHostnameToIp(const std::string& hostname, Error* err) const { + InitializeSocketIfNecessary(); + hostent* record = gethostbyname(hostname.c_str()); + if (record == nullptr) { + *err = IOErrorTemplates::kUnableToResolveHostname.Generate(); + return ""; + } + in_addr* address = (in_addr*)(record->h_addr); + std::string ip_address = inet_ntoa(*address); + + *err = nullptr; + return ip_address; +} + +std::unique_ptr<sockaddr_in> SystemIO::BuildSockaddrIn(const std::string& address, Error* err) const { + auto hostname_port_tuple = SplitAddressToHostnameAndPort(address); + if (!hostname_port_tuple) { + *err = IOErrorTemplates::kInvalidAddressFormat.Generate(); + return nullptr; + } + std::string host; + uint16_t port = 0; + std::tie(host, port) = *hostname_port_tuple; + host = ResolveHostnameToIp(host, err); + if(*err != nullptr) { + return nullptr; + } + + short family = AddressFamilyToPosixFamily(AddressFamilies::INET); + if (family == -1) { + *err = IOErrorTemplates::kUnsupportedAddressFamily.Generate(); + return nullptr; + } -} \ No newline at end of file + std::unique_ptr<sockaddr_in> socket_address = std::unique_ptr<sockaddr_in>(new sockaddr_in); + socket_address->sin_addr.s_addr = inet_addr(host.c_str()); + socket_address->sin_port = htons(port); + socket_address->sin_family = family; + + return socket_address; +} + +void hidra2::SystemIO::InetConnect(SocketDescriptor socket_fd, const std::string& address, Error* err) const { + auto socket_address = BuildSockaddrIn(address, err); + if(*err != nullptr) { + return; + } + + if (_connect(socket_fd, socket_address.get(), sizeof(sockaddr_in)) == -1) { + *err = GetLastError(); + // On windows its normal that connect might give an "WSAEWOULDBLOCK" error, + // since the socket need time to be created + if (*err != nullptr && IOErrorTemplates::kResourceTemporarilyUnavailable != *err) { + return; + } + } + *err = nullptr; +} + +std::unique_ptr<std::tuple<std::string, SocketDescriptor>> SystemIO::InetAcceptConnection(SocketDescriptor socket_fd, +Error* err) const { + static short family = AddressFamilyToPosixFamily(AddressFamilies::INET); + if (family == -1) { + *err = IOErrorTemplates::kUnsupportedAddressFamily.Generate(); + return nullptr; + } + + sockaddr_in client_address{}; + static size_t client_address_size = sizeof(sockaddr_in); + + int peer_fd; + while (true) { + peer_fd = _accept(socket_fd, reinterpret_cast<sockaddr*>(&client_address), &client_address_size); + + if (peer_fd == -1) { + *err = GetLastError(); + if (*err != nullptr && IOErrorTemplates::kResourceTemporarilyUnavailable != *err) { + continue; + } + return nullptr; + } + break; + } + + *err = nullptr; + ApplyNetworkOptions(peer_fd, err); + + std::string + address = std::string(inet_ntoa(client_address.sin_addr)) + ':' + std::to_string(client_address.sin_port); + return std::unique_ptr<std::tuple<std::string, SocketDescriptor>>(new + std::tuple<std::string, + SocketDescriptor>( + address, + peer_fd)); +} + +hidra2::FileDescriptor hidra2::SystemIO::Open(const std::string& filename, + int open_flags, + Error* err) const { + int flags = FileOpenModeToPosixFileOpenMode(open_flags); + FileDescriptor fd = _open(filename.c_str(), flags); + if(fd == -1) { + *err = GetLastError(); + (*err)->Append(filename); + } else { + *err = nullptr; + } + return fd; +} + +void hidra2::SystemIO::CloseSocket(SocketDescriptor fd, Error* err) const { + if(err) { + *err = nullptr; + } + if(!_close_socket(fd) && err) { + *err = GetLastError(); + } +} + +void hidra2::SystemIO::Close(FileDescriptor fd, Error* err) const { + if(err) { + *err = nullptr; + } + if(!_close(fd) && err) { + *err = GetLastError(); + } +} + +short hidra2::SystemIO::AddressFamilyToPosixFamily(AddressFamilies address_family) const { + switch (address_family) { + case AddressFamilies::INET: + return AF_INET; + } + return -1; +} + +int hidra2::SystemIO::SocketTypeToPosixType(SocketTypes socket_type) const { + switch (socket_type) { + case SocketTypes::STREAM: + return SOCK_STREAM; + } + return -1; +} + +int hidra2::SystemIO::SocketProtocolToPosixProtocol(SocketProtocols socket_protocol) const { + switch (socket_protocol) { + case SocketProtocols::IP: + return IPPROTO_IP; + } + return -1; +} + +SocketDescriptor SystemIO::CreateSocket(AddressFamilies address_family, + SocketTypes socket_type, + SocketProtocols socket_protocol, + Error* err) const { + int domain = AddressFamilyToPosixFamily(address_family); + if(domain == -1) { + *err = IOErrorTemplates::kUnsupportedAddressFamily.Generate(); + return -1; + } + + int type = SocketTypeToPosixType(socket_type); + if(type == -1) { + *err = IOErrorTemplates::kUnknownIOError.Generate(); + return -1; + } + + int protocol = SocketProtocolToPosixProtocol(socket_protocol); + if(protocol == -1) { + *err = IOErrorTemplates::kUnknownIOError.Generate(); + return -1; + } + + SocketDescriptor socket_fd = _socket(domain, type, protocol); + if(socket_fd == -1) { + *err = GetLastError(); + return socket_fd; + } + + *err = nullptr; + + ApplyNetworkOptions(socket_fd, err); + + return socket_fd; +} + +void hidra2::SystemIO::InetBind(SocketDescriptor socket_fd, const std::string& address, + Error* err) const { + *err = nullptr; + + int family = AddressFamilyToPosixFamily(AddressFamilies::INET); + if (family == -1) { + *err = IOErrorTemplates::kUnsupportedAddressFamily.Generate(); + return; + } + + auto socket_address = BuildSockaddrIn(address, err); + if(*err != nullptr) { + return; + } + + if (::bind(socket_fd, reinterpret_cast<const sockaddr*>(socket_address.get()), sizeof(sockaddr_in)) == -1) { + *err = GetLastError(); + } +} + +void hidra2::SystemIO::Listen(SocketDescriptor socket_fd, int backlog, Error* err) const { + *err = nullptr; + + if (_listen(socket_fd, backlog) == -1) { + *err = GetLastError(); + } +} + +SocketDescriptor SystemIO::CreateAndBindIPTCPSocketListener(const std::string& address, int backlog, Error* err) const { + FileDescriptor listener_fd = CreateSocket(AddressFamilies::INET, SocketTypes::STREAM, SocketProtocols::IP, err); + + if(*err) { + return -1; + } + + InetBind(listener_fd, address, err); + if(*err) { + CloseSocket(listener_fd, nullptr); + return -1; + } + + Listen(listener_fd, backlog, err); + if(*err) { + CloseSocket(listener_fd, nullptr); + return -1; + } + + return listener_fd; +} + +size_t hidra2::SystemIO::ReceiveWithTimeout(SocketDescriptor socket_fd, void* buf, size_t length, long timeout_in_usec, + Error* err) const { + *err = nullptr; + + fd_set read_fds; + FD_ZERO(&read_fds); + FD_SET(socket_fd, &read_fds); + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = timeout_in_usec; + + int res = ::select(socket_fd + 1, &read_fds, nullptr, nullptr, &timeout); + if (res == 0) { + *err = IOErrorTemplates::kTimeout.Generate(); + return 0; + } + if (res == -1) { + *err = GetLastError(); + return 0; + } + + return Receive(socket_fd, buf, length, err); +} + + +size_t hidra2::SystemIO::Read(FileDescriptor fd, void* buf, size_t length, Error* err) const { + return Transfer(_read, fd, buf, length, err); +} + +size_t hidra2::SystemIO::Write(FileDescriptor fd, const void* buf, size_t length, Error* err) const { + return Transfer(_write, fd, buf, length, err); +} + +size_t hidra2::SystemIO::Receive(SocketDescriptor socket_fd, void* buf, size_t length, Error* err) const { + return Transfer(_recv, socket_fd, buf, length, err); +} + +size_t hidra2::SystemIO::Send(SocketDescriptor socket_fd, const void* buf, size_t length, Error* err) const { + return Transfer(_send, socket_fd, buf, length, err); +} + +size_t SystemIO::Transfer(ssize_t (* method)(FileDescriptor, const void*, size_t), + FileDescriptor fd, + const void* buf, + size_t length, + Error* err) const { + return Transfer(reinterpret_cast<ssize_t (*)(FileDescriptor, void*, size_t)>(method), fd, + const_cast<void*>(buf), length, err); +} + +size_t SystemIO::Transfer(ssize_t (* method)(FileDescriptor, void*, size_t), FileDescriptor fd, void* buf, + size_t length, Error* err) const { + *err = nullptr; + size_t already_transferred = 0; + + while (already_transferred < length) { + ssize_t received_amount = method(fd, (uint8_t*) buf + already_transferred, length - already_transferred); + if (received_amount == 0) { + *err = ErrorTemplates::kEndOfFile.Generate(); + return already_transferred; + } + if (received_amount == -1) { + *err = GetLastError(); + if(IOErrorTemplates::kResourceTemporarilyUnavailable == *err) { + continue; + } + if(*err == nullptr) { + *err = IOErrorTemplates::kUnknownIOError.Generate(); + } + return already_transferred;//Return the amount of _ensured_ transferred bytes + } + already_transferred += received_amount; + } + *err = nullptr; + return already_transferred; +} + +} diff --git a/common/cpp/src/system_io/system_io.h b/common/cpp/src/system_io/system_io.h new file mode 100644 index 0000000000000000000000000000000000000000..5fb98136244570c99fa71e4ec23728eddd68c222 --- /dev/null +++ b/common/cpp/src/system_io/system_io.h @@ -0,0 +1,111 @@ +#ifndef HIDRA2_SYSTEM__SYSTEM_IO_H +#define HIDRA2_SYSTEM__SYSTEM_IO_H + +#include "../../include/io/io.h" + +#ifdef _WIN32 +#include <windows.h> +#undef GetObject +#undef max +#undef min +typedef SSIZE_T ssize_t; +#endif + +#if defined(__linux__) || defined (__APPLE__) +#include "../../../../../../../../usr/include/netinet/in.h" +#endif + +namespace hidra2 { + +class SystemIO final : public IO { + private: + static const int kNetBufferSize;//TODO: need to set by config + + void ApplyNetworkOptions(SocketDescriptor socket_fd, Error* err) const; + + //void CollectFileInformationRecursively(const std::string& path, std::vector<FileInfo>* files, IOErrors* err) const; + int FileOpenModeToPosixFileOpenMode(int open_flags) const; + Error GetLastError() const; + + short AddressFamilyToPosixFamily (AddressFamilies address_family) const; + int SocketTypeToPosixType (SocketTypes socket_type) const; + int SocketProtocolToPosixProtocol (SocketProtocols socket_protocol) const; + + // System function mapping. Should only be called by the wrapper function + FileDescriptor _open(const char* filename, int posix_open_flags) const; + bool _close(FileDescriptor fd) const; + int _mkdir(const char* dirname) const; + + SocketDescriptor _socket(int address_family, int socket_type, int socket_protocol) const; + SocketDescriptor _connect(SocketDescriptor socket_fd, const void* address, size_t address_length) const; + int _listen(SocketDescriptor socket_fd, int backlog) const; + SocketDescriptor _accept(SocketDescriptor socket_fd, void* address, size_t* address_length) const; + bool _close_socket(SocketDescriptor socket_fd) const; + + void InitializeSocketIfNecessary() const; + std::unique_ptr<std::tuple<std::string, uint16_t>> SplitAddressToHostnameAndPort(std::string address) const; + + FileInfo GetFileInfo(const std::string& name, Error* err) const; + + std::unique_ptr<sockaddr_in> BuildSockaddrIn(const std::string& address, Error* err) const; + + /* + * Transfer functions + */ + size_t Transfer(ssize_t (*method)(FileDescriptor, const void*, size_t), FileDescriptor fd, const void* buf, + size_t length, + Error* err) const; + size_t Transfer(ssize_t (*method)(FileDescriptor, void*, size_t), FileDescriptor fd, void* buf, size_t length, + Error* err) const; + static ssize_t _send(SocketDescriptor socket_fd, const void* buffer, size_t length); + static ssize_t _recv(SocketDescriptor socket_fd, void* buffer, size_t length); + static ssize_t _read(FileDescriptor fd, void* buffer, size_t length); + static ssize_t _write(FileDescriptor fd, const void* buffer, size_t count); + + public: + /* + * Special + */ + std::unique_ptr<std::thread> NewThread(std::function<void()> function) const; + + + // this is not standard function - to be implemented differently in windows and linux + std::vector<FileInfo> FilesInFolder(const std::string& folder, Error* err) const; + + /* + * Network + */ + SocketDescriptor CreateSocket(AddressFamilies address_family, SocketTypes socket_type, SocketProtocols socket_protocol, + Error* err) const; + void Listen(SocketDescriptor socket_fd, int backlog, Error* err) const; + void InetBind(SocketDescriptor socket_fd, const std::string& address, Error* err) const; + SocketDescriptor CreateAndBindIPTCPSocketListener(const std::string& address, int backlog, Error* err) const; + std::unique_ptr<std::tuple<std::string, SocketDescriptor>> InetAcceptConnection(SocketDescriptor socket_fd, + Error* err) const; + std::string ResolveHostnameToIp(const std::string& hostname, Error* err) const; + void InetConnect(SocketDescriptor socket_fd, const std::string& address, Error* err) const; + SocketDescriptor CreateAndConnectIPTCPSocket(const std::string& address, Error* err) const; + size_t Receive(SocketDescriptor socket_fd, void* buf, size_t length, Error* err) const; + size_t ReceiveWithTimeout(SocketDescriptor socket_fd, void* buf, size_t length, long timeout_in_usec, + Error* err) const; + size_t Send(SocketDescriptor socket_fd, const void* buf, size_t length, Error* err) const; + void Skip(SocketDescriptor socket_fd, size_t length, Error* err) const; + void CloseSocket(SocketDescriptor socket_fd, Error* err) const; + + /* + * Filesystem + */ + FileDescriptor Open(const std::string& filename, int open_flags, Error* err) const; + void Close(FileDescriptor fd, Error* err) const; + size_t Read(FileDescriptor fd, void* buf, size_t length, Error* err) const; + size_t Write(FileDescriptor fd, const void* buf, size_t length, Error* err) const; + void CreateNewDirectory(const std::string& directory_name, Error* err) const; + FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const; + Error WriteDataToFile (const std::string& fname, const FileData& data, size_t length) const; + void CollectFileInformationRecursively(const std::string& path, std::vector<FileInfo>* files, + Error* err) const; + std::string ReadFileToString(const std::string& fname, Error* err) const; +}; +} + +#endif //HIDRA2_SYSTEM__SYSTEM_IO_H diff --git a/common/cpp/src/system_io/system_io_linux.cpp b/common/cpp/src/system_io/system_io_linux.cpp index 9a3653f285f97a99b455d0ef313115fa45b6325d..e0faf78b744ddc6becc4e556d2d575ec9e6fdcf6 100644 --- a/common/cpp/src/system_io/system_io_linux.cpp +++ b/common/cpp/src/system_io/system_io_linux.cpp @@ -1,15 +1,18 @@ -#include "system_wrappers/system_io.h" #include <cstring> #include <dirent.h> #include <sys/stat.h> #include <algorithm> - +#include <netinet/in.h> +#include <arpa/inet.h> #include <fcntl.h> +#include <iostream> +#include <zconf.h> +#include <netdb.h> + +#include "system_io.h" -#include <cerrno> -#include <unistd.h> using std::string; using std::vector; @@ -17,6 +20,56 @@ using std::chrono::system_clock; namespace hidra2 { +/** + * \defgroup SYSTEM_IO_LINUX_PRIVATE + * Local and private function that are being used by system_io_linux.cpp + * @{ + */ + +Error GetLastErrorFromErrno() { + switch (errno) { + case 0: + return nullptr; + case EBADF: + return IOErrorTemplates::kBadFileNumber.Generate(); + case EAGAIN: + return IOErrorTemplates::kResourceTemporarilyUnavailable.Generate(); + case ENOENT: + case ENOTDIR: + return IOErrorTemplates::kFileNotFound.Generate(); + case EACCES: + return IOErrorTemplates::kPermissionDenied.Generate(); + case EFAULT: + return IOErrorTemplates::kInvalidMemoryAddress.Generate(); + case EEXIST: + return IOErrorTemplates::kFileAlreadyExists.Generate(); + case ENOSPC: + return IOErrorTemplates::kNoSpaceLeft.Generate(); + case ECONNREFUSED: + return IOErrorTemplates::kConnectionRefused.Generate(); + case EADDRINUSE: + return IOErrorTemplates::kAddressAlreadyInUse.Generate(); + case ECONNRESET: + return IOErrorTemplates::kConnectionResetByPeer.Generate(); + case ENOTSOCK: + return IOErrorTemplates::kSocketOperationOnNonSocket.Generate(); + case ENOPROTOOPT: + return IOErrorTemplates::kSocketOperationUnknownAtLevel.Generate(); + case EDOM: + return IOErrorTemplates::kSocketOperationValueOutOfBound.Generate(); + + default: + std::cout << "[IOErrorsFromErrno] Unknown error code: " << errno << std::endl; + Error err = IOErrorTemplates::kUnknownIOError.Generate(); + (*err).Append("Unknown error code: " + std::to_string(errno)); + return err; + } +}; + +Error SystemIO::GetLastError() const { + return GetLastErrorFromErrno(); +} + bool IsDirectory(const struct dirent* entity) { return entity->d_type == DT_DIR && strstr(entity->d_name, "..") == nullptr && @@ -50,12 +103,12 @@ struct stat FileStat(const string& fname, Error* err) { errno = 0; int res = stat(fname.c_str(), &t_stat); if (res < 0) { - *err = IOErrorFromErrno(); + *err = GetLastErrorFromErrno(); } return t_stat; } -FileInfo GetFileInfo(const string& name, Error* err) { +FileInfo GetFileInfo(const string& name, Error* err) { FileInfo file_info; SetFileName(name, &file_info); @@ -73,6 +126,10 @@ FileInfo GetFileInfo(const string& name, Error* err) { return file_info; } +FileInfo SystemIO::GetFileInfo(const string& name, Error* err) const { + return ::hidra2::GetFileInfo(name, err); +} + void ProcessFileEntity(const struct dirent* entity, const std::string& path, FileInfos* files, Error* err) { @@ -88,24 +145,22 @@ void ProcessFileEntity(const struct dirent* entity, const std::string& path, files->push_back(file_info); } -void SystemIO::CollectFileInformationRecursivly(const std::string& path, - FileInfos* files, Error* err) const { +/** @} */ + +void SystemIO::CollectFileInformationRecursively(const std::string& path, + FileInfos* files, Error* err) const { errno = 0; auto dir = opendir((path).c_str()); if (dir == nullptr) { - *err = IOErrorFromErrno(); + *err = GetLastError(); (*err)->Append(path); return; } - while (true) { - errno = 0; - struct dirent* current_entity = readdir(dir); - if (!current_entity) { - break; - } + + while (struct dirent* current_entity = readdir(dir)) { if (IsDirectory(current_entity)) { - CollectFileInformationRecursivly(path + "/" + current_entity->d_name, - files, err); + CollectFileInformationRecursively(path + "/" + current_entity->d_name, + files, err); } else { ProcessFileEntity(current_entity, path, files, err); } @@ -115,26 +170,77 @@ void SystemIO::CollectFileInformationRecursivly(const std::string& path, return; } } - *err = IOErrorFromErrno(); + *err = GetLastError(); closedir(dir); } +void SystemIO::ApplyNetworkOptions(SocketDescriptor socket_fd, Error* err) const { + //TODO: Need to change network layer code, so everything can be NonBlocking + // in use and one have to wait for some time until the system cleans up the stuff + int flag; + if ( + /*(flags = fcntl(socket_fd, F_GETFL, 0)) == -1 + || + fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK) == -1 + ||*/ + setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, (char*)&kNetBufferSize, sizeof(kNetBufferSize)) != 0 + || + setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) != 0 + ) { + *err = GetLastError(); + } +} + +FileDescriptor SystemIO::_open(const char* filename, int posix_open_flags) const { + return ::open(filename, posix_open_flags, S_IWUSR | S_IRWXU); +} -int64_t SystemIO::read(int __fd, void* buf, size_t count) const noexcept { - return (int64_t) ::read(__fd, buf, count); +bool SystemIO::_close(hidra2::FileDescriptor fd) const { + return ::close(fd) == 0; } -int64_t SystemIO::write(int __fd, const void* __buf, size_t __n) const noexcept { - return (int64_t) ::write(__fd, __buf, __n); +ssize_t SystemIO::_read(hidra2::FileDescriptor fd, void* buffer, size_t length) { + return ::read(fd, buffer, length); } -int SystemIO::open(const char* __file, int __oflag) const noexcept { - return ::open(__file, __oflag); +ssize_t SystemIO::_write(hidra2::FileDescriptor fd, const void* buffer, size_t length) { + return ::write(fd, buffer, length); } -int SystemIO::close(int __fd) const noexcept { - return ::close(__fd); +SocketDescriptor SystemIO::_socket(int address_family, int socket_type, int socket_protocol) const { + return ::socket(address_family, socket_type, socket_protocol); } +ssize_t SystemIO::_send(SocketDescriptor socket_fd, const void* buffer, size_t length) { + return ::send(socket_fd, buffer, length, MSG_DONTWAIT); +} + +ssize_t SystemIO::_recv(SocketDescriptor socket_fd, void* buffer, size_t length) { + return ::recv(socket_fd, buffer, length, MSG_DONTWAIT); +} + +int SystemIO::_mkdir(const char* dirname) const { + return ::mkdir(dirname, S_IRWXU); +} + +int SystemIO::_listen(SocketDescriptor socket_fd, int backlog) const { + return ::listen(socket_fd, backlog); +} + +SocketDescriptor SystemIO::_connect(SocketDescriptor socket_fd, const void* address, size_t address_length) const { + return ::connect(socket_fd, static_cast<const sockaddr*>(address), static_cast<socklen_t>(address_length)); +} + +SocketDescriptor SystemIO::_accept(SocketDescriptor socket_fd, void* address, size_t* address_length) const { + return ::accept(socket_fd, static_cast<sockaddr*>(address), reinterpret_cast<socklen_t*>(address_length)); +} + +bool SystemIO::_close_socket(SocketDescriptor socket_fd) const { + return ::close(socket_fd) == 0; +} + +void SystemIO::InitializeSocketIfNecessary() const { + +} } diff --git a/common/cpp/src/system_io/system_io_windows.cpp b/common/cpp/src/system_io/system_io_windows.cpp index 19be8a1509bf07e4b1e82d935a5362564234aef6..de37715511c647da449d85c7649a0c96560157d4 100644 --- a/common/cpp/src/system_io/system_io_windows.cpp +++ b/common/cpp/src/system_io/system_io_windows.cpp @@ -1,12 +1,13 @@ -#include "system_wrappers/system_io.h" +#include "system/system_io.h" #include <cstring> #include <sys/stat.h> #include <algorithm> #include <io.h> #include <windows.h> - #include <fcntl.h> +#include <iostream> +#include <direct.h> using std::string; @@ -16,26 +17,45 @@ using std::chrono::system_clock; namespace hidra2 { Error IOErrorFromGetLastError() { - const char* message; - switch (GetLastError()) { - case ERROR_SUCCESS : + DWORD last_error = GetLastError(); + switch (last_error) { + case ERROR_SUCCESS: return nullptr; case ERROR_PATH_NOT_FOUND: case ERROR_FILE_NOT_FOUND: - message = IOErrors::kFileNotFound; - break; + return IOErrorTemplates::kFileNotFound.Generate(); case ERROR_ACCESS_DENIED: - message = IOErrors::kPermissionDenied; - break; + return IOErrorTemplates::kPermissionDenied.Generate(); + case ERROR_CONNECTION_REFUSED: + return IOErrorTemplates::kConnectionRefused.Generate(); + case WSAEFAULT: + return IOErrorTemplates::kInvalidMemoryAddress.Generate(); + case WSAECONNRESET: + return IOErrorTemplates::kConnectionResetByPeer.Generate(); + case WSAENOTSOCK: + return IOErrorTemplates::kSocketOperationOnNonSocket.Generate(); + case WSAEWOULDBLOCK: + return IOErrorTemplates::kResourceTemporarilyUnavailable.Generate(); + case WSAEADDRNOTAVAIL: + return IOErrorTemplates::kAddressNotValid.Generate(); + case WSAECONNREFUSED: + return IOErrorTemplates::kConnectionRefused.Generate(); + case ERROR_FILE_EXISTS: + return IOErrorTemplates::kFileAlreadyExists.Generate(); default: - message = IOErrors::kUnknownError; - break; + std::cout << "[IOErrorFromGetLastError] Unknown error code: " << last_error << std::endl; + Error err = IOErrorTemplates::kUnknownIOError.Generate(); + (*err).Append("Unknown error code: " + std::to_string(last_error)); + return err; } - return TextError(message); +} + +Error SystemIO::GetLastError() const { + return IOErrorFromGetLastError(); } Error CheckFileTime(const FILETIME& ft) { - SYSTEMTIME st = {0}; + SYSTEMTIME st = { 0 }; if (!FileTimeToSystemTime(&ft, &st)) { return IOErrorFromGetLastError(); } @@ -85,7 +105,7 @@ bool IsDirectory(const WIN32_FIND_DATA f) { strstr(f.cFileName, ".") == nullptr; } -FileInfo GetFileInfo_win(const WIN32_FIND_DATA& f, const string& name, Error* err) { +FileInfo GetFileInfo_win(const WIN32_FIND_DATA& f, const string& name, Error* err) { FileInfo file_info; file_info.modify_date = FileTime2TimePoint(f.ftLastWriteTime, err); @@ -105,10 +125,10 @@ FileInfo GetFileInfo_win(const WIN32_FIND_DATA& f, const string& name, Error* er } -FileInfo GetFileInfo(const string& name, Error* err) { +FileInfo SystemIO::GetFileInfo(const std::string& name, Error* err) const { WIN32_FIND_DATA f; - auto hFind = FindFirstFile(name.c_str() , &f); + auto hFind = FindFirstFile(name.c_str(), &f); if (hFind == INVALID_HANDLE_VALUE) { *err = IOErrorFromGetLastError(); (*err)->Append(name); @@ -118,7 +138,6 @@ FileInfo GetFileInfo(const string& name, Error* err) { return GetFileInfo_win(f, name, err); } - void ProcessFileEntity(const WIN32_FIND_DATA& f, const std::string& path, FileInfos* files, Error* err) { @@ -135,9 +154,8 @@ void ProcessFileEntity(const WIN32_FIND_DATA& f, const std::string& path, files->push_back(file_info); } - -void SystemIO::CollectFileInformationRecursivly(const std::string& path, - FileInfos* files, Error* err) const { +void SystemIO::CollectFileInformationRecursively(const std::string& path, + FileInfos* files, Error* err) const { WIN32_FIND_DATA find_data; HANDLE handle = FindFirstFile((path + "\\*.*").c_str(), &find_data); if (handle == INVALID_HANDLE_VALUE) { @@ -148,7 +166,7 @@ void SystemIO::CollectFileInformationRecursivly(const std::string& path, do { if (IsDirectory(find_data)) { - CollectFileInformationRecursivly(path + "\\" + find_data.cFileName, files, err); + CollectFileInformationRecursively(path + "\\" + find_data.cFileName, files, err); } else { ProcessFileEntity(find_data, path, files, err); } @@ -163,25 +181,95 @@ void SystemIO::CollectFileInformationRecursivly(const std::string& path, } else { *err = IOErrorFromGetLastError(); } -} -int64_t SystemIO::read(int __fd, void* buf, size_t count) const noexcept { - return (int64_t) _read(__fd, buf, (unsigned int) count); } -int64_t SystemIO::write(int __fd, const void* __buf, size_t __n) const noexcept { - return (int64_t) _write(__fd, __buf, (unsigned int) __n); +void hidra2::SystemIO::ApplyNetworkOptions(SocketDescriptor socket_fd, Error* err) const { + //TODO: Seeing issues when using these settings - need further investigation + //Event if NonBlockingIO is set, it seems that _recv is a blocking call :/ + /* + static u_long iMode = 1; + + if ( + ioctlsocket(socket_fd, FIONBIO, &iMode) != 0 + || + setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, (char*)&kNetBufferSize, sizeof(kNetBufferSize)) != 0 + || + setsockopt(socket_fd, SOL_SOCKET, SO_SNDBUF, (char*)&kNetBufferSize, sizeof(kNetBufferSize)) != 0 + ) { + *err = GetLastError(); + } + */ } -int SystemIO::open(const char* __file, int __oflag) const noexcept { +FileDescriptor SystemIO::_open(const char* filename, int posix_open_flags) const { int fd; - errno = _sopen_s(&fd, __file, __oflag | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE); + errno = _sopen_s(&fd, filename, posix_open_flags | _O_BINARY, _SH_DENYNO, _S_IREAD | _S_IWRITE); return fd; } -int SystemIO::close(int __fd) const noexcept { - return ::_close(__fd); +bool SystemIO::_close(FileDescriptor fd) const { + return ::_close(fd) == 0; +} + +bool SystemIO::_close_socket(SocketDescriptor fd) const { + return ::closesocket(fd) == 0; +} + +SocketDescriptor SystemIO::_socket(int address_family, int socket_type, int socket_protocol) const { + InitializeSocketIfNecessary(); + return ::socket(address_family, socket_type, socket_protocol); +} + +SocketDescriptor SystemIO::_connect(SocketDescriptor socket_fd, const void* address, size_t address_length) const { + return ::connect(socket_fd, static_cast<const sockaddr*>(address), address_length); +} + +ssize_t SystemIO::_read(FileDescriptor fd, void* buffer, size_t length) { + return ::_read(fd, (char*)buffer, length); } +ssize_t SystemIO::_write(FileDescriptor fd, const void* buffer, size_t length) { + return ::_write(fd, (const char*)buffer, length); +} + +ssize_t SystemIO::_send(SocketDescriptor socket_fd, const void* buffer, size_t length) { + return ::send(socket_fd, (char*)buffer, length, 0); +} + +ssize_t SystemIO::_recv(SocketDescriptor socket_fd, void* buffer, size_t length) { + return ::recv(socket_fd, (char*)buffer, length, 0); +} + +int SystemIO::_mkdir(const char* dirname) const { + return ::_mkdir(dirname); +} + +int SystemIO::_listen(SocketDescriptor fd, int backlog) const { + return ::listen(fd, backlog); +} + +SocketDescriptor SystemIO::_accept(SocketDescriptor socket_fd, void* address, size_t* address_length) const { + return ::accept(socket_fd, static_cast<sockaddr*>(address), (int*)address_length); +} + +void SystemIO::InitializeSocketIfNecessary() const { + static bool WSAStartupDone = false; + if (!WSAStartupDone) { + WSAStartupDone = true; + WORD wVersionRequested = MAKEWORD(2, 2); + WSADATA wsaData; + int err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + std::cout << "[_socket/WSAStartup] Failed to WSAStartup with version 2.2" << std::endl; + WSACleanup(); + // Do not return, since ::socket has to set an errno + } else { + std::atexit([] { + WSACleanup(); + }); + } + } +} } diff --git a/common/cpp/unittests/common/test_error.cpp b/common/cpp/unittests/common/test_error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9055fb3ef7b6b9f8914bba43f45e3e39938ba052 --- /dev/null +++ b/common/cpp/unittests/common/test_error.cpp @@ -0,0 +1,26 @@ +#include <gmock/gmock.h> +#include <common/error.h> +#include "gtest/gtest.h" + +using hidra2::Error; +using ::testing::Eq; +using ::testing::Ne; + +namespace { + +TEST(ErrorTemplate, GenerateNoNullptr) { + Error error = hidra2::ErrorTemplates::kEndOfFile.Generate(); + ASSERT_THAT(error, Ne(nullptr)); +} + +TEST(ErrorTemplate, EqCheck) { + Error error = hidra2::ErrorTemplates::kEndOfFile.Generate(); + ASSERT_TRUE(hidra2::ErrorTemplates::kEndOfFile == error); +} + + +TEST(ErrorTemplate, NeCheck) { + Error error = hidra2::ErrorTemplates::kEndOfFile.Generate(); + ASSERT_FALSE(hidra2::ErrorTemplates::kMemoryAllocationError == error); +} +} diff --git a/common/cpp/unittests/json_parser/test_json_parser.cpp b/common/cpp/unittests/json_parser/test_json_parser.cpp index d4545a5a0129152ee7b03099cab941113db5be53..0767fd02065553e54cafbc86556183eb40702eec 100644 --- a/common/cpp/unittests/json_parser/test_json_parser.cpp +++ b/common/cpp/unittests/json_parser/test_json_parser.cpp @@ -73,7 +73,6 @@ TEST(ParseString, DoubleEmbeddedConvertToJson) { ASSERT_THAT(id, Eq(2)); } - TEST(ParseString, ErrorOnWrongEmbeddedKey) { std::string json = R"({"id1":{"test":2}})"; @@ -98,7 +97,6 @@ TEST(ParseString, ErrorOnWrongEmbeddedSubKey) { ASSERT_THAT(err->Explain(), ::testing::HasSubstr("cannot find")); } - TEST(ParseString, ErrorOnWrongKey) { std::string json = R"({"_id":"2"})"; @@ -150,7 +148,6 @@ TEST(ParseString, IntArrayConvertToJson) { ASSERT_THAT(vec, ElementsAre(1, 2, 3)); } - TEST(ParseString, IntArrayErrorConvertToJson) { std::string json = R"({"array":[1,2,"3"]})"; @@ -176,7 +173,6 @@ TEST(ParseString, StringArrayConvertToJson) { ASSERT_THAT(vec, ElementsAre("s1", "s2", "s3")); } - class ParseFileTests : public Test { public: RapidJson parser{"filename", true}; @@ -193,27 +189,24 @@ TEST_F(ParseFileTests, CorrectConvertFileToJson) { std::string json = R"({"_id":2})"; EXPECT_CALL(mock_io, ReadFileToString_t("filename", _)). - WillOnce(DoAll(testing::SetArgPointee<1>(static_cast<hidra2::SimpleError*>(nullptr)), testing::Return(json))); + WillOnce(DoAll(testing::SetArgPointee<1>(nullptr), testing::Return(json))); uint64_t id; auto err = parser.GetUInt64("_id", &id); ASSERT_THAT(id, Eq(2)); } - - TEST_F(ParseFileTests, CannotReadFile) { std::string json = R"({"_id":2})"; EXPECT_CALL(mock_io, ReadFileToString_t("filename", _)). - WillOnce(DoAll(testing::SetArgPointee<1>(new hidra2::SimpleError(hidra2::IOErrors::kFileNotFound)), + WillOnce(DoAll(testing::SetArgPointee<1>(hidra2::IOErrorTemplates::kFileNotFound.Generate().release()), testing::Return(""))); uint64_t id; auto err = parser.GetUInt64("_id", &id); - ASSERT_THAT(err->Explain(), HasSubstr(hidra2::IOErrors::kFileNotFound)); + ASSERT_THAT(err->Explain(), HasSubstr(hidra2::IOErrorTemplates::kFileNotFound.Generate()->Explain())); } - -} \ No newline at end of file +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b80aa24186ce17fa1d7ab8e967ad48791c4e5472..ae70ed32ef8c56c1562e5103f200dfc441234863 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(worker) +add_subdirectory(producer) diff --git a/examples/producer/CMakeLists.txt b/examples/producer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cac4b634fb8194953cd74efe056680b1dd0722ff --- /dev/null +++ b/examples/producer/CMakeLists.txt @@ -0,0 +1,2 @@ + +add_subdirectory(dummy-data-producer) diff --git a/examples/producer/dummy-data-producer/CMakeLists.txt b/examples/producer/dummy-data-producer/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..27851c5910a02919caf96222df6df338548eab74 --- /dev/null +++ b/examples/producer/dummy-data-producer/CMakeLists.txt @@ -0,0 +1,34 @@ +set(TARGET_NAME dummy-data-producer) +set(SOURCE_FILES + dummy_data_producer.cpp + ) + +add_executable(${TARGET_NAME} ${SOURCE_FILES}) +target_include_directories(${TARGET_NAME} PUBLIC include ${CMAKE_SOURCE_DIR}/common/cpp/include) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES}) + +target_link_libraries(${TARGET_NAME} producer-api) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + +if (CMAKE_COMPILER_IS_GNUCXX) + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage") +endif() + +set (dir examples/${TARGET_NAME}) +install(TARGETS ${TARGET_NAME} DESTINATION "${dir}") +install(FILES ${SOURCE_FILES} DESTINATION "${dir}") + +configure_file(CMakeLists_separate.in CMakeLists_separate.txt @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeLists_separate.txt DESTINATION "${dir}" RENAME CMakeLists.txt) + +configure_file(Makefile.in Makefile_LINUX @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Makefile_LINUX DESTINATION "${dir}") + +IF(WIN32) +add_script_test("${TARGET_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/Debug/${TARGET_NAME}") +ELSE() +add_script_test("${TARGET_NAME}" "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}") +ENDIF() diff --git a/examples/producer/dummy-data-producer/CMakeLists_separate.in b/examples/producer/dummy-data-producer/CMakeLists_separate.in new file mode 100644 index 0000000000000000000000000000000000000000..f0b390f039dd5aefe673005ce6ef99138a70e84f --- /dev/null +++ b/examples/producer/dummy-data-producer/CMakeLists_separate.in @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 2.8) + +project(@TARGET_NAME@) + +set(CMAKE_CXX_STANDARD 11) + +IF(CMAKE_C_COMPILER_ID STREQUAL "GNU") + SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libgcc -static-libstdc++") +ENDIF() + +find_package (Threads) + +set(TARGET_NAME ${CMAKE_PROJECT_NAME}) + +set(SOURCE_FILES @SOURCE_FILES@) + +link_directories(@CMAKE_INSTALL_PREFIX@/lib) + +add_executable(${TARGET_NAME} ${SOURCE_FILES}) +target_include_directories(${TARGET_NAME} PUBLIC @CMAKE_INSTALL_PREFIX@/include) +target_link_libraries(${TARGET_NAME} producer-api ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/producer/dummy-data-producer/Makefile.in b/examples/producer/dummy-data-producer/Makefile.in new file mode 100644 index 0000000000000000000000000000000000000000..824fb1e946bb2beadd4b4082beeb86640cd2c83c --- /dev/null +++ b/examples/producer/dummy-data-producer/Makefile.in @@ -0,0 +1,25 @@ +PROGRAM=@TARGET_NAME@ + +CXX=g++ +CXXFLAGS=-std=c++11 +LDFLAGS=-pthread -static-libgcc -static-libstdc++ +LIBS=-L @CMAKE_INSTALL_PREFIX@/lib -lproducer-api +INCLUDE=-I @CMAKE_INSTALL_PREFIX@/include +RM=rm -f + +SRCS=@SOURCE_FILES@ +OBJS=$(subst .cpp,.o,$(SRCS)) + +all: $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) + +%.o: %.cpp + $(CXX) $(CXXFLAGS) $(INCLUDE) -c -o $@ $< + +clean: + $(RM) $(OBJS) + +distclean: clean + $(RM) $(PROGRAM) diff --git a/examples/producer/dummy-data-producer/check_linux.sh b/examples/producer/dummy-data-producer/check_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..04f592decbf9d22d1301157df898e0f9c2b764fa --- /dev/null +++ b/examples/producer/dummy-data-producer/check_linux.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +database_name=test_run + +#set -e + + +#just test that it starts, no reciever is running +$@ 0.0.0.0 1 1 2>&1 | grep "refused" + diff --git a/examples/producer/dummy-data-producer/check_windows.bat b/examples/producer/dummy-data-producer/check_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..95652e449db3fa550ce10cac2b8040f74a4b54e0 --- /dev/null +++ b/examples/producer/dummy-data-producer/check_windows.bat @@ -0,0 +1,9 @@ +"%1" 0.0.0.0 1 1 2>&1 | findstr "not valid" || goto :error +goto :clean + +:error +call :clean +exit /b 1 + +:clean + diff --git a/examples/producer/dummy-data-producer/dummy_data_producer.cpp b/examples/producer/dummy-data-producer/dummy_data_producer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9476d52422276874a68b692957d4d034c19191b --- /dev/null +++ b/examples/producer/dummy-data-producer/dummy_data_producer.cpp @@ -0,0 +1,71 @@ +#include <iostream> +#include <chrono> +#include <vector> +#include <tuple> + +#include "hidra2_producer.h" + +using std::chrono::high_resolution_clock; + +typedef std::tuple<std::string, size_t, uint64_t> ArgumentTuple; +ArgumentTuple ProcessCommandArguments(int argc, char* argv[]) { + if (argc != 4) { + std::cout << + "Usage: " << argv[0] << " <receiver_address> <number_of_byte> <iterations>" + << std::endl; + exit(EXIT_FAILURE); + } + try { + return ArgumentTuple(argv[1], std::stoull(argv[2]), std::stoull(argv[3])); + } catch(std::exception& e) { + std::cerr << "Fail to parse arguments" << std::endl; + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + } +} + +bool SendDummyData(hidra2::Producer* producer, size_t number_of_byte, uint64_t iterations) { + auto buffer = std::unique_ptr<uint8_t>(new uint8_t[number_of_byte]); + + for(uint64_t i = 0; i < iterations; i++) { + std::cout << "Send file " << i + 1 << "/" << iterations << std::endl; + + auto err = producer->Send(i, buffer.get(), number_of_byte); + + if (err) { + std::cerr << "File was not successfully send: " << err << std::endl; + return false; + } else { + std::cout << "File was successfully send." << std::endl; + } + } + + return true; +} + +int main (int argc, char* argv[]) { + std::string receiver_address; + size_t number_of_byte; + uint64_t iterations; + std::tie(receiver_address, number_of_byte, iterations) = ProcessCommandArguments(argc, argv); + + std::cout << "receiver_address: " << receiver_address << std::endl + << "number_of_byte: " << number_of_byte << std::endl + << "iterations: " << iterations << std::endl + << std::endl; + + auto producer = hidra2::Producer::Create(); + auto err = producer->ConnectToReceiver(receiver_address); + if(err) { + std::cerr << "Failed to connect to receiver. ProducerError: " << err << std::endl; + return EXIT_FAILURE; + } + std::cout << "Successfully connected" << std::endl; + + if(!SendDummyData(producer.get(), number_of_byte, iterations)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + diff --git a/examples/worker/CMakeLists.txt b/examples/worker/CMakeLists.txt index 631b7d02a64b3ef4a11578a767b4d33b2df0d8aa..70c8090c095cbc0c297538a0f4e3b0e74ddef854 100644 --- a/examples/worker/CMakeLists.txt +++ b/examples/worker/CMakeLists.txt @@ -3,4 +3,3 @@ find_package(Threads) add_subdirectory(process_folder) add_subdirectory(getnext_broker) - diff --git a/examples/worker/getnext_broker/check_linux.sh b/examples/worker/getnext_broker/check_linux.sh index d4b766bc430880ca310e37608a0822100b1ad005..b00865d7b4e2533221ce2c40f447ce559dce9abc 100644 --- a/examples/worker/getnext_broker/check_linux.sh +++ b/examples/worker/getnext_broker/check_linux.sh @@ -24,6 +24,5 @@ do echo 'db.data.insert({"_id":'$i',"size":100,"name":"'$i'","lastchange":1})' | mongo ${database_name} done -$args 127.0.0.1:5005 $database_name 2 -#| grep "Processed 3 file(s)" +$args 127.0.0.1:5005 $database_name 2 | grep "Processed 3 file(s)" diff --git a/examples/worker/getnext_broker/getnext_broker.cpp b/examples/worker/getnext_broker/getnext_broker.cpp index 0f578333b51ffb444c7ca0204a9d11e87883c66c..3c5a43d0677555276b0f25aaf2aed1768272c7fe 100644 --- a/examples/worker/getnext_broker/getnext_broker.cpp +++ b/examples/worker/getnext_broker/getnext_broker.cpp @@ -20,7 +20,7 @@ void WaitThreads(std::vector<std::thread>* threads) { void ProcessError(const Error& err) { if (err == nullptr) return; - if (err->GetErrorType() != hidra2::ErrorType::kEOF) { + if (err->GetErrorType() != hidra2::ErrorType::kEndOfFile) { std::cout << err->Explain() << std::endl; exit(EXIT_FAILURE); } diff --git a/examples/worker/process_folder/CMakeLists.txt b/examples/worker/process_folder/CMakeLists.txt index 0c0d05d73cb46d7295e078d39f74090acd9a3a95..ea20503288ce7e01cc57dd3ff121905981d6703d 100644 --- a/examples/worker/process_folder/CMakeLists.txt +++ b/examples/worker/process_folder/CMakeLists.txt @@ -2,7 +2,12 @@ set(TARGET_NAME worker_processfolder) set(SOURCE_FILES process_folder.cpp) add_executable(${TARGET_NAME} ${SOURCE_FILES}) -target_link_libraries(${TARGET_NAME} hidra2-worker ${CMAKE_THREAD_LIBS_INIT}) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES}) + +target_link_libraries(${TARGET_NAME} hidra2-worker) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) #use expression generator to get rid of VS adding Debug/Release folders set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY diff --git a/examples/worker/process_folder/check_linux.sh b/examples/worker/process_folder/check_linux.sh old mode 100644 new mode 100755 diff --git a/examples/worker/process_folder/process_folder.cpp b/examples/worker/process_folder/process_folder.cpp index fd789d2fdf1df19cde1564c4b7fe85f03669e286..f85f7257385f15b092f5598ee7e4e84daa5b95d6 100644 --- a/examples/worker/process_folder/process_folder.cpp +++ b/examples/worker/process_folder/process_folder.cpp @@ -60,7 +60,7 @@ void ReadAllData(std::unique_ptr<hidra2::DataBroker>* broker, Statistics* statis nfiles++; size += file_info.size; } - if (err->GetErrorType() != hidra2::ErrorType::kEOF) { + if (err->GetErrorType() != hidra2::ErrorType::kEndOfFile) { std::cout << err->Explain() << std::endl; exit(EXIT_FAILURE); } diff --git a/producer/CMakeLists.txt b/producer/CMakeLists.txt index 0d50d291afaac3aedb56552609d99d712a59c8a1..53d257b04ea6ffc4b663736b9d8bd559dc341222 100644 --- a/producer/CMakeLists.txt +++ b/producer/CMakeLists.txt @@ -1 +1 @@ -add_subdirectory(inotify-event-detector-cpp) +add_subdirectory(api) diff --git a/producer/api/CMakeLists.txt b/producer/api/CMakeLists.txt index db938ae4c1ad7dde2364954b8600eee6d27cc698..2c9da5a959a00b8ef2069aaf71d1c9479ce18c7f 100644 --- a/producer/api/CMakeLists.txt +++ b/producer/api/CMakeLists.txt @@ -1,5 +1,9 @@ set(TARGET_NAME producer-api) -set(SOURCE_FILES src/producer/producer.cpp include/producer/producer.h) +set(SOURCE_FILES + src/producer.cpp + src/producer_impl.h + src/producer_impl.cpp + ) ################################ @@ -8,14 +12,20 @@ set(SOURCE_FILES src/producer/producer.cpp include/producer/producer.h) add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>) target_include_directories(${TARGET_NAME} PUBLIC include) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) ################################ # Testing ################################ -set(TEST_SOURCE_FILES unittests/test_producer.cpp) -set(TEST_LIBRARIES producer-api) +set(TEST_SOURCE_FILES + unittests/test_producer_impl.cpp + unittests/test_producer.cpp + ) +set(TEST_LIBRARIES "${TARGET_NAME}") -gtest(${TARGET_NAME} ${TEST_SOURCE_FILES} ${TEST_LIBRARIES}) +gtest(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}") +install(TARGETS ${TARGET_NAME} DESTINATION lib) +install(DIRECTORY include/ DESTINATION include) diff --git a/producer/api/include/hidra2_producer.h b/producer/api/include/hidra2_producer.h new file mode 100644 index 0000000000000000000000000000000000000000..7cb5d2431367beccc455cecbc333c804414ac66c --- /dev/null +++ b/producer/api/include/hidra2_producer.h @@ -0,0 +1,8 @@ +#ifndef HIDRA2_HIDRA2_PRODUCER_H +#define HIDRA2_HIDRA2_PRODUCER_H + +#include "common/io_error.h" +#include "producer/producer.h" + + +#endif //HIDRA2_HIDRA2_PRODUCER_H diff --git a/producer/api/include/producer/producer.h b/producer/api/include/producer/producer.h index 674836c52d330d3c662efe3204ca33027a1d25de..3f7583875ece1fe62017cd9445865a0b25e905eb 100644 --- a/producer/api/include/producer/producer.h +++ b/producer/api/include/producer/producer.h @@ -1,21 +1,53 @@ -#ifndef HIDRA2__PRODUCER_PRODUCER_H -#define HIDRA2__PRODUCER_PRODUCER_H +#ifndef HIDRA2_PRODUCER__PRODUCER_H +#define HIDRA2_PRODUCER__PRODUCER_H +#include <memory> #include <string> +#include "producer_error.h" + namespace hidra2 { + +enum class ProducerStatus { + kDisconnected, + kConnected, +}; + class Producer { - private: - static unsigned long kinit_count_; - Producer(); public: - static const uint32_t VERSION; + //! Creates a new producer + /*! + * @return A unique_ptr to a new producer instance + */ + static std::unique_ptr<Producer> Create(); + + virtual ~Producer() = default; + + /*! + * @return The version of the producer + */ + virtual uint64_t GetVersion() const = 0; - Producer(const Producer&) = delete; - Producer& operator=(const Producer&) = delete; + /*! + * @return The current status of the producer + */ + virtual ProducerStatus GetStatus() const = 0; - static Producer* CreateProducer(std::string receiver_address); + //! Connects to a receiver + /*! + \param receiver_address - The address of the receiver. E.g. 127.0.0.1:4200 + \return Error - nullptr on success + */ + virtual Error ConnectToReceiver(const std::string& receiver_address) = 0; + //! Sends data to the receiver + /*! + \param file_id - The id of the file. An error will be returned if this file id already exists on the receiver. + \param data - A pointer to the data to send + \param file_size - The size of the data. + \return Error - Will be nullptr on success + */ + virtual Error Send(uint64_t file_id, const void* data, size_t file_size) = 0; }; } -#endif //HIDRA2__PRODUCER_PRODUCER_H +#endif //HIDRA2_PRODUCER__PRODUCER_H diff --git a/producer/api/include/producer/producer_error.h b/producer/api/include/producer/producer_error.h new file mode 100644 index 0000000000000000000000000000000000000000..cdcf69a742ad54dd6d248f654e56953ce6faf701 --- /dev/null +++ b/producer/api/include/producer/producer_error.h @@ -0,0 +1,78 @@ +#ifndef HIDRA2_PRODUCER_ERROR_H +#define HIDRA2_PRODUCER_ERROR_H + +#include "common/error.h" + +namespace hidra2 { + +enum class ProducerErrorType { + kAlreadyConnected, + kConnectionNotReady, + kFileTooLarge, + kFileIdAlreadyInUse, + kUnknownServerError +}; + +//TODO Make a marco to create error class and error template class +class ProducerError : public SimpleError { + private: + ProducerErrorType receiver_error_type_; + public: + ProducerError(const std::string& error, ProducerErrorType receiver_error_type) : SimpleError(error, + ErrorType::kProducerError) { + receiver_error_type_ = receiver_error_type; + } + + ProducerErrorType GetProducerErrorType() const noexcept { + return receiver_error_type_; + } +}; + +class ProducerErrorTemplate : public SimpleErrorTemplate { + protected: + ProducerErrorType receiver_error_type_; + public: + ProducerErrorTemplate(const std::string& error, ProducerErrorType receiver_error_type) : SimpleErrorTemplate(error, + ErrorType::kProducerError) { + receiver_error_type_ = receiver_error_type; + } + + inline ProducerErrorType GetProducerErrorType() const noexcept { + return receiver_error_type_; + } + + inline Error Generate() const noexcept override { + return Error(new ProducerError(error_, receiver_error_type_)); + } + + inline bool operator==(const Error& rhs) const override { + return SimpleErrorTemplate::operator==(rhs) + && GetProducerErrorType() == ((ProducerError*) rhs.get())->GetProducerErrorType(); + } +}; + +namespace ProducerErrorTemplates { +auto const kAlreadyConnected = ProducerErrorTemplate { + "Already connected", ProducerErrorType::kAlreadyConnected +}; +auto const kConnectionNotReady = ProducerErrorTemplate { + "Connection not ready", ProducerErrorType::kConnectionNotReady +}; + +auto const kFileTooLarge = ProducerErrorTemplate { + "File too large", ProducerErrorType::kFileTooLarge +}; + +auto const kFileIdAlreadyInUse = ProducerErrorTemplate { + "File already in use", ProducerErrorType::kFileIdAlreadyInUse +}; + +auto const kUnknownServerError = ProducerErrorTemplate { + "Unknown server error", ProducerErrorType::kUnknownServerError +}; + + +}; +} + +#endif //HIDRA2_PRODUCER_ERROR_H diff --git a/producer/api/src/producer.cpp b/producer/api/src/producer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25add6276cc3bf432fc4a8a07069e112890b384a --- /dev/null +++ b/producer/api/src/producer.cpp @@ -0,0 +1,6 @@ +#include "producer/producer.h" +#include "producer_impl.h" + +std::unique_ptr<hidra2::Producer> hidra2::Producer::Create() { + return std::unique_ptr<hidra2::Producer>(new ProducerImpl()); +} diff --git a/producer/api/src/producer/producer.cpp b/producer/api/src/producer/producer.cpp deleted file mode 100644 index f4373a91db6b1098bc85595a3567f7b3de5577df..0000000000000000000000000000000000000000 --- a/producer/api/src/producer/producer.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include <producer/producer.h> - -unsigned long hidra2::Producer::kinit_count_ = 0; -const uint32_t hidra2::Producer::VERSION = 1; - -hidra2::Producer::Producer() { - kinit_count_++; -} - -hidra2::Producer* hidra2::Producer::CreateProducer(std::string receiver_address) { - return new Producer(); -} diff --git a/producer/api/src/producer_impl.cpp b/producer/api/src/producer_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3ec4e49a7b2b145fd6b62d40c95743010afd163 --- /dev/null +++ b/producer/api/src/producer_impl.cpp @@ -0,0 +1,113 @@ +#include <iostream> +#include <cstring> + +#include "producer_impl.h" +#include "io/io_factory.h" + +namespace hidra2 { + +const uint32_t ProducerImpl::kVersion = 1; +const size_t ProducerImpl::kMaxChunkSize = size_t(1024) * size_t(1024) * size_t(1024) * size_t(2); //2GiByte + +ProducerImpl::ProducerImpl(): io__{GenerateDefaultIO()} { +} + +uint64_t ProducerImpl::GetVersion() const { + return kVersion; +} + +ProducerStatus ProducerImpl::GetStatus() const { + return status_; +} + +Error ProducerImpl::InitializeSocketToReceiver(const std::string& receiver_address) { + Error err; + FileDescriptor fd = io__->CreateAndConnectIPTCPSocket(receiver_address, &err); + if(err != nullptr) { + return err; + } + + client_fd_ = fd; + return nullptr; +} + +Error ProducerImpl::ConnectToReceiver(const std::string& receiver_address) { + if(status_ != ProducerStatus::kDisconnected) { + return ProducerErrorTemplates::kAlreadyConnected.Generate(); + } + + auto error = InitializeSocketToReceiver(receiver_address); + if(error) { + status_ = ProducerStatus::kDisconnected; + return error; + } + + status_ = ProducerStatus::kConnected; + return nullptr; +} + +GenericNetworkRequestHeader ProducerImpl::GenerateNextSendRequest(uint64_t file_id, size_t file_size) { + GenericNetworkRequestHeader request; + request.op_code = kNetOpcodeSendData; + request.request_id = request_id_++; + request.data_id = file_id; + request.data_size = file_size; + return request; +} + +Error ProducerImpl::SendHeaderAndData(const GenericNetworkRequestHeader& header, const void* data, size_t file_size) { + Error io_error; + io__->Send(client_fd_, &header, sizeof(header), &io_error); + if(io_error) { + std::cerr << "ProducerImpl::Send/DataRequest error" << io_error << std::endl; + return io_error; + } + + io__->Send(client_fd_, data, file_size, &io_error); + if(io_error) { + std::cerr << "ProducerImpl::Send/data error" << io_error << std::endl; + return io_error; + } + + return nullptr; +} + +Error ProducerImpl::ReceiveResponce() { + Error err; + SendDataResponse sendDataResponse; + io__->Receive(client_fd_, &sendDataResponse, sizeof(sendDataResponse), &err); + if(err != nullptr) { + std::cerr << "ProducerImpl::Receive error: " << err << std::endl; + return err; + } + + if(sendDataResponse.error_code) { + if(sendDataResponse.error_code == kNetErrorFileIdAlreadyInUse) { + return ProducerErrorTemplates::kFileIdAlreadyInUse.Generate(); + } + std::cerr << "Server reported an error. NetErrorCode: " << int(sendDataResponse.error_code) << std::endl; + return ProducerErrorTemplates::kUnknownServerError.Generate(); + } + return nullptr; +} + + +Error ProducerImpl::Send(uint64_t file_id, const void* data, size_t file_size) { + if(status_ != ProducerStatus::kConnected) { + return ProducerErrorTemplates::kConnectionNotReady.Generate(); + } + if(file_size > kMaxChunkSize) { + return ProducerErrorTemplates::kFileTooLarge.Generate(); + } + + auto send_data_request = GenerateNextSendRequest(file_id, file_size); + + auto error = SendHeaderAndData(send_data_request, data, file_size); + if(error) { + return error; + } + + return ReceiveResponce(); +} + +} \ No newline at end of file diff --git a/producer/api/src/producer_impl.h b/producer/api/src/producer_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..05797c5058dc06d7fca68bf001f71b0be2365d34 --- /dev/null +++ b/producer/api/src/producer_impl.h @@ -0,0 +1,39 @@ +#ifndef HIDRA2_PRODUCER__PRODUCER_IMPL_H +#define HIDRA2_PRODUCER__PRODUCER_IMPL_H + +#include <string> +#include <common/networking.h> +#include <io/io.h> +#include "producer/producer.h" + +namespace hidra2 { +class ProducerImpl : public Producer { + private: + static const uint32_t kVersion; + + int client_fd_ = -1; + uint64_t request_id_ = 0; + + ProducerStatus status_ = ProducerStatus::kDisconnected; + + Error InitializeSocketToReceiver(const std::string& receiver_address); + GenericNetworkRequestHeader GenerateNextSendRequest(uint64_t file_id, size_t file_size); + Error SendHeaderAndData(const GenericNetworkRequestHeader& header, const void* data, size_t file_size); + Error ReceiveResponce(); + + public: + static const size_t kMaxChunkSize; + + ProducerImpl(); + ProducerImpl(const ProducerImpl&) = delete; + ProducerImpl& operator=(const ProducerImpl&) = delete; + + uint64_t GetVersion() const override; + ProducerStatus GetStatus() const override; + Error ConnectToReceiver(const std::string& receiver_address) override; + Error Send(uint64_t file_id, const void* data, size_t file_size) override; + std::unique_ptr<IO> io__; +}; +} + +#endif //HIDRA2_PRODUCER__PRODUCER_IMPL_H diff --git a/producer/api/unittests/test_producer.cpp b/producer/api/unittests/test_producer.cpp index 6522702215d4ce9f78a110dba4983c71c9e1dc69..91b1f691a6a541cef3da6f69a694c87353574b20 100644 --- a/producer/api/unittests/test_producer.cpp +++ b/producer/api/unittests/test_producer.cpp @@ -1,14 +1,16 @@ #include <gtest/gtest.h> -#include <producer/producer.h> +#include <unittests/MockIO.h> + +#include "producer/producer.h" +#include "../src/producer_impl.h" +using ::testing::Ne; namespace { -TEST(VERSION, VersionAboveZero) { - EXPECT_GE(hidra2::Producer::VERSION, 0); -} TEST(CreateProducer, PointerIsNotNullptr) { - hidra2::Producer* prod = hidra2::Producer::CreateProducer("127.0.0.1"); - EXPECT_NE(prod, nullptr); - delete prod; + std::unique_ptr<hidra2::Producer> producer = hidra2::Producer::Create(); + ASSERT_THAT(dynamic_cast<hidra2::ProducerImpl*>(producer.get()), Ne(nullptr)); + ASSERT_THAT(producer.get(), Ne(nullptr)); } + } diff --git a/producer/api/unittests/test_producer_impl.cpp b/producer/api/unittests/test_producer_impl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3733166493d6c6397c7229f2f3767bf4cac6421c --- /dev/null +++ b/producer/api/unittests/test_producer_impl.cpp @@ -0,0 +1,275 @@ +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <unittests/MockIO.h> + +#include "common/error.h" +#include "io/io.h" +#include "producer/producer.h" +#include "../src/producer_impl.h" + +namespace { +using ::testing::Return; +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgReferee; +using ::testing::Gt; +using ::testing::Eq; +using ::testing::Mock; +using ::testing::InSequence; + +TEST(get_version, VersionAboveZero) { + hidra2::ProducerImpl producer; + EXPECT_GE(producer.GetVersion(), 0); +} + + +/** + * ConnectToReceiver + */ + +class ProducerImpl : public testing::Test { + public: + hidra2::ProducerImpl producer; + testing::NiceMock<hidra2::MockIO> mock_io; + hidra2::FileDescriptor expected_fd = 83942; + uint64_t expected_file_id = 4224; + std::string expected_address = "127.0.0.1:9090"; + uint64_t expected_request_id = 0; + uint64_t expected_file_size = 1337; + void* expected_file_pointer = (void*)0xC00FE; + + void SetUp() override { + producer.io__ = std::unique_ptr<hidra2::IO> {&mock_io}; + } + void TearDown() override { + producer.io__.release(); + } + + void ConnectToReceiver_DONE(hidra2::FileDescriptor expected_fd = 1) { + EXPECT_CALL(mock_io, CreateAndConnectIPTCPSocket_t(_, _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<1>(nullptr), + Return(expected_fd) + )); + producer.ConnectToReceiver(""); + } + void Send_DONE(int times = 1) { + EXPECT_CALL(mock_io, Send_t(_, _, _, _)) + .Times(times) + .WillRepeatedly( + DoAll( + testing::SetArgPointee<3>(nullptr), + testing::ReturnArg<2>() + )); + } +}; + +TEST_F(ProducerImpl, get_status__disconnected) { + hidra2::ProducerStatus status = producer.GetStatus(); + ASSERT_THAT(status, Eq(hidra2::ProducerStatus::kDisconnected)); +} + + +TEST_F(ProducerImpl, ConnectToReceiver__CreateAndConnectIPTCPSocket_error) { + EXPECT_CALL(mock_io, CreateAndConnectIPTCPSocket_t(expected_address, _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<1>(hidra2::IOErrorTemplates::kInvalidAddressFormat.Generate().release()), + Return(-1) + )); + + auto error = producer.ConnectToReceiver(expected_address); + auto status = producer.GetStatus(); + + ASSERT_THAT(error, Eq(hidra2::IOErrorTemplates::kInvalidAddressFormat)); + ASSERT_THAT(status, Eq(hidra2::ProducerStatus::kDisconnected)); +} + +TEST_F(ProducerImpl, ConnectToReceiver) { + EXPECT_CALL(mock_io, CreateAndConnectIPTCPSocket_t(expected_address, _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<1>(nullptr), + Return(expected_fd) + )); + + auto error = producer.ConnectToReceiver(expected_address); + auto status = producer.GetStatus(); + + ASSERT_THAT(error, Eq(nullptr)); + ASSERT_THAT(status, Eq(hidra2::ProducerStatus::kConnected)); +} + +TEST_F(ProducerImpl, ConnectToReceiver__already_connected) { + InSequence sequence; + + ConnectToReceiver_DONE(); + + auto error = producer.ConnectToReceiver(expected_address); + + ASSERT_THAT(error, Eq(hidra2::ProducerErrorTemplates::kAlreadyConnected)); +} + +/** + * Send + */ + +MATCHER_P3(M_CheckSendDataRequest, request_id, file_id, file_size, + "Checks if a valid GenericNetworkRequestHeader was Send") { + return ((hidra2::GenericNetworkRequestHeader*)arg)->op_code == hidra2::kNetOpcodeSendData + && ((hidra2::GenericNetworkRequestHeader*)arg)->request_id == request_id + && ((hidra2::GenericNetworkRequestHeader*)arg)->data_id == file_id + && ((hidra2::GenericNetworkRequestHeader*)arg)->data_size == file_size; +} + +ACTION_P2(A_WriteSendDataResponse, error_code, request_id) { + ((hidra2::SendDataResponse*)arg1)->op_code = hidra2::kNetOpcodeSendData; + ((hidra2::SendDataResponse*)arg1)->error_code = error_code; + ((hidra2::SendDataResponse*)arg1)->request_id = request_id; +} + +TEST_F(ProducerImpl, Send__connection_not_ready) { + + auto error = producer.Send(expected_file_id, nullptr, 1); + + ASSERT_THAT(error, Eq(hidra2::ProducerErrorTemplates::kConnectionNotReady)); +} + +TEST_F(ProducerImpl, Send__file_too_large) { + + ConnectToReceiver_DONE(expected_fd); + + auto error = producer.Send(expected_file_id, nullptr, + size_t(1024) * size_t(1024) * size_t(1024) * size_t(3)); + + ASSERT_THAT(error, Eq(hidra2::ProducerErrorTemplates::kFileTooLarge)); +} + +TEST_F(ProducerImpl, Send__sendDataRequest_error) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + + EXPECT_CALL(mock_io, Send_t(expected_fd, M_CheckSendDataRequest(expected_request_id, expected_file_id, + expected_file_size), + sizeof(hidra2::GenericNetworkRequestHeader), _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(hidra2::IOErrorTemplates::kBadFileNumber.Generate().release()), + Return(-1) + )); + + auto error = producer.Send(expected_file_id, nullptr, expected_file_size); + + ASSERT_THAT(error, Eq(hidra2::IOErrorTemplates::kBadFileNumber)); +} + +TEST_F(ProducerImpl, Send__sendData_error) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + Send_DONE(); + + EXPECT_CALL(mock_io, Send_t(expected_fd, expected_file_pointer, expected_file_size, _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(hidra2::IOErrorTemplates::kBadFileNumber.Generate().release()), + Return(-1) + )); + + + auto error = producer.Send(expected_file_id, expected_file_pointer, expected_file_size); + + ASSERT_THAT(error, Eq(hidra2::IOErrorTemplates::kBadFileNumber)); +} + + +TEST_F(ProducerImpl, Send__Receive_error) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + Send_DONE(2); + + EXPECT_CALL(mock_io, Receive_t(expected_fd, _, sizeof(hidra2::SendDataResponse), _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(hidra2::IOErrorTemplates::kBadFileNumber.Generate().release()), + testing::Return(-1) + )); + + auto error = producer.Send(expected_file_id, expected_file_pointer, expected_file_size); + + ASSERT_THAT(error, Eq(hidra2::IOErrorTemplates::kBadFileNumber)); +} + +TEST_F(ProducerImpl, Send__Receive_server_error) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + Send_DONE(2); + + + EXPECT_CALL(mock_io, Receive_t(_, _, sizeof(hidra2::SendDataResponse), _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(nullptr), + A_WriteSendDataResponse(hidra2::kNetErrorAllocateStorageFailed, expected_request_id), + testing::ReturnArg<2>() + )); + + auto error = producer.Send(expected_file_id, expected_file_pointer, expected_file_size); + + ASSERT_THAT(error, Eq(hidra2::ProducerErrorTemplates::kUnknownServerError)); +} + +TEST_F(ProducerImpl, Send__Receive_server_error_id_already_in_use) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + Send_DONE(2); + + + EXPECT_CALL(mock_io, Receive_t(_, _, sizeof(hidra2::SendDataResponse), _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(nullptr), + A_WriteSendDataResponse(hidra2::kNetErrorFileIdAlreadyInUse, expected_request_id), + testing::ReturnArg<2>() + )); + + auto error = producer.Send(expected_file_id, expected_file_pointer, expected_file_size); + + ASSERT_THAT(error, Eq(hidra2::ProducerErrorTemplates::kFileIdAlreadyInUse)); +} + +TEST_F(ProducerImpl, Send) { + InSequence sequence; + + ConnectToReceiver_DONE(expected_fd); + Send_DONE(2); + + + EXPECT_CALL(mock_io, Receive_t(_, _, sizeof(hidra2::SendDataResponse), _)) + .Times(1) + .WillOnce( + DoAll( + testing::SetArgPointee<3>(nullptr), + A_WriteSendDataResponse(hidra2::kNetErrorNoError, expected_request_id), + testing::ReturnArg<2>() + )); + + auto error = producer.Send(expected_file_id, expected_file_pointer, expected_file_size); + + ASSERT_THAT(error, Eq(nullptr)); +} + +} diff --git a/producer/inotify-event-detector-cpp/CMakeLists.txt b/producer/inotify-event-detector-cpp/CMakeLists.txt deleted file mode 100644 index dcb6da5431f4b6f4bd99004f33daeb122739dcdd..0000000000000000000000000000000000000000 --- a/producer/inotify-event-detector-cpp/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -set(TARGET_NAME inotify-event-detector-cpp) -set(SOURCE_FILES src/main.cpp) - - -################################ -# Executable and link -################################ -add_executable(${TARGET_NAME} ${SOURCE_FILES}) -target_include_directories(${TARGET_NAME} PRIVATE include) -target_link_libraries(${TARGET_NAME} common producer-api) -set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) - -################################ -# Testing -################################ -set(TEST_SOURCE_FILES unittests/test_inotify_client.cpp) - - -#gtest(${TARGET_NAME} ${TEST_SOURCE_FILES} "") diff --git a/producer/inotify-event-detector-cpp/src/main.cpp b/producer/inotify-event-detector-cpp/src/main.cpp deleted file mode 100644 index 62d77f036cc0be9f6da7bd1728fbe931c97e95db..0000000000000000000000000000000000000000 --- a/producer/inotify-event-detector-cpp/src/main.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include <producer/producer.h> -#include <iostream> - -int main (int argc, char* argv[]) { - std::cout << "Running producer version: " << hidra2::Producer::VERSION << std::endl; - - hidra2::Producer* producer = hidra2::Producer::CreateProducer("127.0.0.1"); - if(!producer) { - std::cerr << "Fail to create producer" << std::endl; - return 1; - } - - std::cout << "Successfully create producer " << std::hex << producer << std::endl; - - return 0; -} diff --git a/producer/inotify-event-detector-cpp/unittests/test_inotify_client.cpp b/producer/inotify-event-detector-cpp/unittests/test_inotify_client.cpp deleted file mode 100644 index 1e1b911ef88017e2c98dc5915b52b210f2e18fbf..0000000000000000000000000000000000000000 --- a/producer/inotify-event-detector-cpp/unittests/test_inotify_client.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include <gtest/gtest.h> - -TEST(EMPTY, REMOVEME) { - EXPECT_EQ(1, 1); -} diff --git a/receiver/CMakeLists.txt b/receiver/CMakeLists.txt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3fd260ef2dad27c224f83a2311bca432f79626e3 100644 --- a/receiver/CMakeLists.txt +++ b/receiver/CMakeLists.txt @@ -0,0 +1,41 @@ +set(TARGET_NAME receiver) +set(SOURCE_FILES + src/receiver.h src/receiver.cpp + src/connection.h src/connection.cpp + src/receiver_error.h + src/request.cpp + src/request_handler_file_write.cpp + ) + + +################################ +# Library +################################ +add_library(${TARGET_NAME} STATIC ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) +target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) + +add_executable(${TARGET_NAME}-bin src/main.cpp) +set_target_properties(${TARGET_NAME}-bin PROPERTIES OUTPUT_NAME ${TARGET_NAME}) +target_link_libraries(${TARGET_NAME}-bin ${TARGET_NAME}) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME}-bin ${HIDRA2_COMMON_IO_LIBRARIES}) +set_target_properties(${TARGET_NAME}-bin PROPERTIES LINKER_LANGUAGE CXX) + +################################ +# Testing +################################ + +set_property(TARGET ${TARGET_NAME} PROPERTY ENABLE_EXPORTS true) +# +set(TEST_SOURCE_FILES + unittests/test_receiver.cpp + unittests/test_connection.cpp + unittests/test_request.cpp + unittests/test_request_handler_file_write.cpp + ) +# +set(TEST_LIBRARIES "${TARGET_NAME};system_io") +gtest(${TARGET_NAME} "${TEST_SOURCE_FILES}" "${TEST_LIBRARIES}" ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp) diff --git a/receiver/src/connection.cpp b/receiver/src/connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b97358222717c8f4e26dad76f22d4bd56b9a276e --- /dev/null +++ b/receiver/src/connection.cpp @@ -0,0 +1,82 @@ +#include <cstring> +#include <assert.h> +#include "connection.h" +#include "receiver_error.h" +#include "io/io_factory.h" + +namespace hidra2 { + +size_t Connection::kRequestHandlerMaxBufferSize; +std::atomic<uint32_t> Connection::kNetworkProducerPeerImplGlobalCounter(0); + +Connection::Connection(SocketDescriptor socket_fd, const std::string& address): request_factory__{new RequestFactory}, +io__{GenerateDefaultIO()} { + socket_fd_ = socket_fd; + connection_id_ = kNetworkProducerPeerImplGlobalCounter++; + address_ = address; +} + +uint64_t Connection::GetId() const noexcept { + return connection_id_; +} + +NetworkErrorCode GetNetworkCodeFromError(const Error& err) { + if(err) { + if(err == IOErrorTemplates::kFileAlreadyExists) { + return NetworkErrorCode::kNetErrorFileIdAlreadyInUse; + } else { + return NetworkErrorCode::kNetErrorInternalServerError; + } + } + return NetworkErrorCode::kNetErrorNoError; + +} + +Error Connection::ProcessRequest(const std::unique_ptr<Request>& request) const noexcept { + Error err; + err = request->Handle(); + GenericNetworkResponse generic_response; + generic_response.error_code = GetNetworkCodeFromError(err); + if(err) { + std::cerr << "[" << GetId() << "] Error while handling request: " << err << std::endl; + } + io__->Send(socket_fd_, &generic_response, sizeof(GenericNetworkResponse), &err); + return err; +} + + +void Connection::Listen() const noexcept { + while(true) { + Error err; + auto request = WaitForNewRequest(&err); + if(err) { + std::cerr << "[" << GetId() << "] Error while waiting for request: " << err << std::endl; + break; + } + if (!request) continue; //no error, but timeout + err = ProcessRequest(request); + if(err) { + std::cerr << "[" << GetId() << "] Error sending response: " << err << std::endl; + break; + } + } + io__->CloseSocket(socket_fd_, nullptr); + std::cout << "[" << GetId() << "] Disconnected." << std::endl; +} + + +std::unique_ptr<Request> Connection::WaitForNewRequest(Error* err) const noexcept { + //TODO: to be overwritten with MessagePack (or similar) + GenericNetworkRequestHeader generic_request_header; + io__->ReceiveWithTimeout(socket_fd_, &generic_request_header, sizeof(GenericNetworkRequestHeader), 50, err); + if(*err) { + if(*err == IOErrorTemplates::kTimeout) { + *err = nullptr;//Not an error in this case + } + return nullptr; + } + return request_factory__->GenerateRequest(generic_request_header, socket_fd_, err); +} + +} + diff --git a/receiver/src/connection.h b/receiver/src/connection.h new file mode 100644 index 0000000000000000000000000000000000000000..9861ca0d00cdce67138b6cf51f38c8881b94b953 --- /dev/null +++ b/receiver/src/connection.h @@ -0,0 +1,47 @@ +#ifndef HIDRA2_NetworkProducerPeerImpl_H +#define HIDRA2_NetworkProducerPeerImpl_H + +#include "connection.h" + +#include <string> +#include <map> +#include <utility> +#include <thread> +#include <iostream> +#include <atomic> +#include <vector> + +#include "common/networking.h" +#include "io/io.h" +#include "request.h" + +namespace hidra2 { + +class Connection { + public: + private: + uint32_t connection_id_; + std::string address_; + int socket_fd_; + public: + static size_t kRequestHandlerMaxBufferSize; + static std::atomic<uint32_t> kNetworkProducerPeerImplGlobalCounter; + + Connection(SocketDescriptor socket_fd, const std::string& address); + ~Connection() = default; + + void Listen() const noexcept; + uint64_t GetId() const noexcept; + + std::unique_ptr<RequestFactory> request_factory__; + std::unique_ptr<IO> io__; + + private: + std::unique_ptr<Request> WaitForNewRequest(Error* err) const noexcept; + Error ProcessRequest(const std::unique_ptr<Request>& request) const noexcept; +}; + +} + + +#endif //HIDRA2_NetworkProducerPeerImpl_H diff --git a/receiver/src/main.cpp b/receiver/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b871296c3a2838b2fc4561e5e01579380ae74cb --- /dev/null +++ b/receiver/src/main.cpp @@ -0,0 +1,18 @@ +#include <iostream> +#include "receiver.h" + +int main (int argc, char* argv[]) { + static const std::string address = "0.0.0.0:4200"; + + auto* receiver = new hidra2::Receiver(); + + hidra2::Error err; + + std::cout << "Listening on " << address << std::endl; + receiver->Listen(address, &err); + if(err) { + std::cerr << "Failed to start receiver: " << err << std::endl; + return 1; + } + return 0; +} diff --git a/receiver/src/receiver.cpp b/receiver/src/receiver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c614fd1e8856e282a5587c4c058c70fbae2c9c23 --- /dev/null +++ b/receiver/src/receiver.cpp @@ -0,0 +1,67 @@ +#include <cstring> +#include <iostream> +#include "receiver.h" +#include "receiver_error.h" +#include "connection.h" +#include <io/io_factory.h> + +namespace hidra2 { + + +const int Receiver::kMaxUnacceptedConnectionsBacklog = 5; + +Receiver::Receiver(): io__{GenerateDefaultIO()} { + +} + +Error Receiver::PrepareListener(std::string listener_address) { + Error err = nullptr; + listener_fd_ = io__->CreateAndBindIPTCPSocketListener(listener_address, kMaxUnacceptedConnectionsBacklog, + &err); + return err; +} + + +void Receiver::Listen(std::string listener_address, Error* err, bool exit_after_first_connection) { + *err = PrepareListener(listener_address); + if(*err) { + return; + } + + while(true) { + ProcessConnections(err); + if (exit_after_first_connection) break; + } +} + +//TODO: remove error since it is not used +void Receiver::ProcessConnections(Error* err) { + std::string address; + FileDescriptor connection_socket_fd; + + //TODO: Use InetAcceptConnectionWithTimeout + auto client_info_tuple = io__->InetAcceptConnection(listener_fd_, err); + if(*err) { + //TODO: this can produce a lot of error messages + std::cerr << "An error occurred while accepting an incoming connection: " << err << std::endl; + return; + } + std::tie(address, connection_socket_fd) = *client_info_tuple; + StartNewConnectionInSeparateThread(connection_socket_fd, address); +} + +void Receiver::StartNewConnectionInSeparateThread(int connection_socket_fd, const std::string& address) { + auto thread = io__->NewThread([connection_socket_fd, address] { + auto connection = std::unique_ptr<Connection>(new Connection(connection_socket_fd, address)); + std::cout << "[" << connection->GetId() << "] New connection from " << address << std::endl; + connection->Listen(); + }); + + if (thread) { + thread->detach(); + } + return; + +} + +} \ No newline at end of file diff --git a/receiver/src/receiver.h b/receiver/src/receiver.h new file mode 100644 index 0000000000000000000000000000000000000000..a1da1fb4170b7a31a043992d5b0d6ec7ebfeff43 --- /dev/null +++ b/receiver/src/receiver.h @@ -0,0 +1,29 @@ +#ifndef HIDRA2_RECEIVER_H +#define HIDRA2_RECEIVER_H + +#include <string> +#include <thread> +#include "connection.h" +#include <list> + +namespace hidra2 { + +class Receiver { + private: + FileDescriptor listener_fd_ = -1; + Error PrepareListener(std::string listener_address); + void StartNewConnectionInSeparateThread(int connection_socket_fd, const std::string& address); + void ProcessConnections(Error* err); + public: + static const int kMaxUnacceptedConnectionsBacklog;//TODO: Read from config + Receiver(const Receiver&) = delete; + Receiver& operator=(const Receiver&) = delete; + Receiver(); + + void Listen(std::string listener_address, Error* err, bool exit_after_first_connection = false); + std::unique_ptr<IO> io__; +}; + +} + +#endif //HIDRA2_RECEIVER_H diff --git a/receiver/src/receiver_error.h b/receiver/src/receiver_error.h new file mode 100644 index 0000000000000000000000000000000000000000..42eb039d8e5720e23a98dfded74973b788233235 --- /dev/null +++ b/receiver/src/receiver_error.h @@ -0,0 +1,62 @@ +#ifndef HIDRA2_RECEIVER_ERROR_H +#define HIDRA2_RECEIVER_ERROR_H + +#include "common/error.h" + +namespace hidra2 { + +enum class ReceiverErrorType { + kInvalidOpCode, + kBadRequest +}; + +//TODO Make a marco to create error class and error template class +class ReceiverError : public SimpleError { + private: + ReceiverErrorType receiver_error_type_; + public: + ReceiverError(const std::string& error, ReceiverErrorType receiver_error_type) : SimpleError(error, + ErrorType::kReceiverError) { + receiver_error_type_ = receiver_error_type; + } + + ReceiverErrorType GetReceiverErrorType() const noexcept { + return receiver_error_type_; + } +}; + +class ReceiverErrorTemplate : public SimpleErrorTemplate { + protected: + ReceiverErrorType receiver_error_type_; + public: + ReceiverErrorTemplate(const std::string& error, ReceiverErrorType receiver_error_type) : SimpleErrorTemplate(error, + ErrorType::kReceiverError) { + receiver_error_type_ = receiver_error_type; + } + + inline ReceiverErrorType GetReceiverErrorType() const noexcept { + return receiver_error_type_; + } + + inline Error Generate() const noexcept override { + return Error(new ReceiverError(error_, receiver_error_type_)); + } + + inline bool operator==(const Error& rhs) const override { + return SimpleErrorTemplate::operator==(rhs) + && GetReceiverErrorType() == ((ReceiverError*) rhs.get())->GetReceiverErrorType(); + } +}; + +namespace ReceiverErrorTemplates { +auto const kInvalidOpCode = ReceiverErrorTemplate { + "Invalid Opcode", ReceiverErrorType::kInvalidOpCode +}; +auto const kBadRequest = ReceiverErrorTemplate { + "Bad request", ReceiverErrorType::kBadRequest +}; + +}; +} + +#endif //HIDRA2_RECEIVER_ERROR_H diff --git a/receiver/src/request.cpp b/receiver/src/request.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f38d168f1af06cc33e3efae9f3e96bd08a2f453 --- /dev/null +++ b/receiver/src/request.cpp @@ -0,0 +1,88 @@ +#include "request.h" +#include "io/io_factory.h" + +namespace hidra2 { + +Request::Request(const GenericNetworkRequestHeader& header, + SocketDescriptor socket_fd) : io__{GenerateDefaultIO()}, request_header_(header), socket_fd_{socket_fd} { +} + +Error Request::AllocateDataBuffer() { + try { + data_buffer_.reset(new uint8_t[request_header_.data_size]); + } catch(std::exception& e) { + auto err = ErrorTemplates::kMemoryAllocationError.Generate(); + err->Append(e.what()); + return err; + } + return nullptr; +} + +Error Request::ReceiveData() { + auto err = AllocateDataBuffer(); + if (err) { + return err; + } + io__->Receive(socket_fd_, data_buffer_.get(), request_header_.data_size, &err); + return err; +} + + +Error Request::Handle() { + Error err; + if (request_header_.data_size != 0) { + auto err = ReceiveData(); + if (err) { + return err; + } + } + for (auto handler : handlers_) { + auto err = handler->ProcessRequest(*this); + if (err) { + return err; + } + } + return nullptr; +} + +const RequestHandlerList& Request::GetListHandlers() const { + return handlers_; +} + + +void Request::AddHandler(const RequestHandler* handler) { + handlers_.emplace_back(handler); +} + + +uint64_t Request::GetDataSize() const { + return request_header_.data_size; +} + +const FileData& Request::GetData() const { + return data_buffer_; +} + +std::string Request::GetFileName() const { + return std::to_string(request_header_.data_id) + ".bin"; +} + + +std::unique_ptr<Request> RequestFactory::GenerateRequest(const GenericNetworkRequestHeader& + request_header, SocketDescriptor socket_fd, + Error* err) const noexcept { + *err = nullptr; + switch (request_header.op_code) { + case Opcode::kNetOpcodeSendData: { + auto request = std::unique_ptr<Request> {new Request{request_header, socket_fd}}; + request->AddHandler(&request_handler_filewrite_); + return request; + } + default: + *err = ReceiverErrorTemplates::kInvalidOpCode.Generate(); + return nullptr; + } + +} + +} \ No newline at end of file diff --git a/receiver/src/request.h b/receiver/src/request.h new file mode 100644 index 0000000000000000000000000000000000000000..3d8ef1025dddc481ee9bd2dca594668f93367eab --- /dev/null +++ b/receiver/src/request.h @@ -0,0 +1,45 @@ +#ifndef HIDRA2_REQUEST_H +#define HIDRA2_REQUEST_H + +#include "receiver_error.h" +#include "common/networking.h" +#include "io/io.h" +#include "request_handler.h" +#include "request_handler_file_write.h" + +namespace hidra2 { + +using RequestHandlerList = std::vector<const RequestHandler*>; + +class Request { + public: + virtual Error Handle(); + virtual ~Request() = default; + Request(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd); + void AddHandler(const RequestHandler*); + const RequestHandlerList& GetListHandlers() const; + virtual uint64_t GetDataSize() const; + virtual std::string GetFileName() const; + + virtual const FileData& GetData() const; + std::unique_ptr<IO> io__; + private: + Error AllocateDataBuffer(); + Error ReceiveData(); + const GenericNetworkRequestHeader request_header_; + const SocketDescriptor socket_fd_; + FileData data_buffer_; + RequestHandlerList handlers_; +}; + +class RequestFactory { + public: + virtual std::unique_ptr<Request> GenerateRequest(const GenericNetworkRequestHeader& request_header, + SocketDescriptor socket_fd, Error* err) const noexcept; + private: + RequestHandlerFileWrite request_handler_filewrite_; +}; + +} + +#endif //HIDRA2_REQUEST_H diff --git a/receiver/src/request_handler.h b/receiver/src/request_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..2d208c636cf6eb8d3e8a62c961ecb6c2db054584 --- /dev/null +++ b/receiver/src/request_handler.h @@ -0,0 +1,19 @@ +#ifndef HIDRA2_REQUEST_HANDLER_H +#define HIDRA2_REQUEST_HANDLER_H + +#include "receiver_error.h" + +namespace hidra2 { + +class Request; + +class RequestHandler { + public: + virtual Error ProcessRequest(const Request& request) const = 0; + virtual ~RequestHandler() = default; + private: +}; + +} + +#endif //HIDRA2_REQUEST_HANDLER_H diff --git a/receiver/src/request_handler_file_write.cpp b/receiver/src/request_handler_file_write.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b1a3c2bc4925502ed2789d3ac3e121f459edebb --- /dev/null +++ b/receiver/src/request_handler_file_write.cpp @@ -0,0 +1,24 @@ +#include "request_handler_file_write.h" +#include "io/io_factory.h" +#include "request.h" +namespace hidra2 { + +Error RequestHandlerFileWrite::ProcessRequest(const Request& request) const { + auto fsize = request.GetDataSize(); + if (fsize <= 0 || fsize > kMaxFileSize) { + return ReceiverErrorTemplates::kBadRequest.Generate(); + } + + const FileData& data = request.GetData(); + + auto fname = request.GetFileName(); +//TODO: folder to write in config file + return io__->WriteDataToFile("files/" + fname, data, fsize); + +} + +RequestHandlerFileWrite::RequestHandlerFileWrite() : io__{GenerateDefaultIO()} { + +} + +} diff --git a/receiver/src/request_handler_file_write.h b/receiver/src/request_handler_file_write.h new file mode 100644 index 0000000000000000000000000000000000000000..8b9828ccd7310c98ea5d9ceaae4c66a4abb12d3f --- /dev/null +++ b/receiver/src/request_handler_file_write.h @@ -0,0 +1,21 @@ +#ifndef HIDRA2_REQUEST_HANDLER_FILE_WRITE_H +#define HIDRA2_REQUEST_HANDLER_FILE_WRITE_H + +#include "request_handler.h" + +#include "io/io.h" + +namespace hidra2 { + +const uint64_t kMaxFileSize = uint64_t(1024) * 1024 * 1024 * 2; //2GB + +class RequestHandlerFileWrite final: public RequestHandler { + public: + RequestHandlerFileWrite(); + Error ProcessRequest(const Request& request) const override; + std::unique_ptr<IO> io__; +}; + +} + +#endif //HIDRA2_REQUEST_HANDLER_FILE_WRITE_H diff --git a/receiver/unittests/test_connection.cpp b/receiver/unittests/test_connection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..846e6df26be5d0283e761d1d1d73cdd54765b18e --- /dev/null +++ b/receiver/unittests/test_connection.cpp @@ -0,0 +1,183 @@ +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <unittests/MockIO.h> +#include "../src/connection.h" +#include "../src/receiver_error.h" +#include "../src/request.h" + +using ::testing::Test; +using ::testing::Return; +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgReferee; +using ::testing::Gt; +using ::testing::Eq; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::SaveArg; +using ::testing::SaveArgPointee; +using ::testing::InSequence; +using ::testing::SetArgPointee; +using ::hidra2::Error; +using ::hidra2::ErrorInterface; +using ::hidra2::FileDescriptor; +using ::hidra2::SocketDescriptor; +using ::hidra2::GenericNetworkRequestHeader; +using ::hidra2::SendDataResponse; +using ::hidra2::GenericNetworkRequestHeader; +using ::hidra2::GenericNetworkResponse; +using ::hidra2::Opcode; +using ::hidra2::Connection; +using ::hidra2::MockIO; +using hidra2::Request; + +namespace { + +class MockRequest: public Request { + public: + MockRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd): + Request(request_header, socket_fd) {}; + Error Handle() override { + return Error{Handle_t()}; + }; + MOCK_CONST_METHOD0(Handle_t, ErrorInterface * ()); +}; + +class MockRequestFactory: public hidra2::RequestFactory { + public: + std::unique_ptr<Request> GenerateRequest(const GenericNetworkRequestHeader& request_header, + SocketDescriptor socket_fd, + Error* err) const noexcept override { + ErrorInterface* error = nullptr; + auto res = GenerateRequest_t(request_header, socket_fd, &error); + err->reset(error); + return std::unique_ptr<Request> {res}; + } + + MOCK_CONST_METHOD3(GenerateRequest_t, Request * (const GenericNetworkRequestHeader&, + SocketDescriptor socket_fd, + ErrorInterface**)); + +}; + +class ConnectionTests : public Test { + public: + Connection connection{0, "some_address"}; + MockIO mock_io; + MockRequestFactory mock_factory; + void SetUp() override { + connection.io__ = std::unique_ptr<hidra2::IO> {&mock_io};; + connection.request_factory__ = std::unique_ptr<hidra2::RequestFactory> {&mock_factory}; + ON_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)). + WillByDefault(DoAll(testing::SetArgPointee<4>(nullptr), + testing::Return(0))); + EXPECT_CALL(mock_io, CloseSocket_t(_, _)); + + } + void TearDown() override { + connection.io__.release(); + connection.request_factory__.release(); + } + +}; + + +TEST_F(ConnectionTests, ErrorWaitForNewRequest) { + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)).Times(2). + WillOnce( + DoAll(SetArgPointee<4>(new hidra2::IOError("", hidra2::IOErrorType::kTimeout)), + Return(0))) + .WillOnce( + DoAll(SetArgPointee<4>(new hidra2::IOError("", hidra2::IOErrorType::kUnknownIOError)), + Return(0)) + ); + + connection.Listen(); +} + +ACTION_P(SaveArg1ToGenericNetworkResponse, value) { + auto resp = *static_cast<const GenericNetworkResponse*>(arg1); + value->error_code = resp.error_code; +} + + +TEST_F(ConnectionTests, CallsHandleRequest) { + + GenericNetworkRequestHeader header; + auto request = new MockRequest{header, 1}; + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); + + EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce( + Return(request) + ); + + EXPECT_CALL(*request, Handle_t()).WillOnce( + Return(new hidra2::SimpleError{""}) + ); + + EXPECT_CALL(mock_io, Send_t(_, _, _, _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + Return(0) + )); + + + connection.Listen(); +} + +TEST_F(ConnectionTests, SendsNoErrorToProducer) { + + GenericNetworkRequestHeader header; + auto request = new MockRequest{header, 1}; + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); + + EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce( + Return(request) + ); + + EXPECT_CALL(*request, Handle_t()).WillOnce( + Return(nullptr) + ); + GenericNetworkResponse response; + EXPECT_CALL(mock_io, Send_t(_, _, sizeof(GenericNetworkResponse), _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + SaveArg1ToGenericNetworkResponse(&response), + Return(0) + )); + + connection.Listen(); + + ASSERT_THAT(response.error_code, Eq(hidra2::NetworkErrorCode::kNetErrorNoError)); +} + +TEST_F(ConnectionTests, SendsErrorToProducer) { + + GenericNetworkRequestHeader header; + auto request = new MockRequest{header, 1}; + + EXPECT_CALL(mock_io, ReceiveWithTimeout_t(_, _, _, _, _)); + + EXPECT_CALL(mock_factory, GenerateRequest_t(_, _, _)).WillOnce( + Return(request) + ); + + EXPECT_CALL(*request, Handle_t()).WillOnce( + Return(new hidra2::SimpleError{""}) + ); + + GenericNetworkResponse response; + EXPECT_CALL(mock_io, Send_t(_, _, sizeof(GenericNetworkResponse), _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)), + SaveArg1ToGenericNetworkResponse(&response), + Return(0) + )); + + connection.Listen(); + + ASSERT_THAT(response.error_code, Eq(hidra2::NetworkErrorCode::kNetErrorInternalServerError)); + +} + +} diff --git a/receiver/unittests/test_receiver.cpp b/receiver/unittests/test_receiver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0226b182bbfca0afb5f0eccdf5ebe89befad3075 --- /dev/null +++ b/receiver/unittests/test_receiver.cpp @@ -0,0 +1,94 @@ +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <unittests/MockIO.h> +#include "../src/receiver.h" +#include "../src/receiver_error.h" +#include "../src/connection.h" + +using ::testing::Return; +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgReferee; +using ::testing::SetArgPointee; +using ::testing::Gt; +using ::testing::Eq; +using ::testing::Mock; +using ::testing::InSequence; +using ::hidra2::Error; +using ::hidra2::FileDescriptor; +using ::hidra2::ErrorInterface; +using ::hidra2::Connection; +using ::hidra2::SocketDescriptor; + +namespace { + +class StartListenerFixture : public testing::Test { + public: + const hidra2::SocketDescriptor expected_socket_descriptor = 20; + const hidra2::SocketDescriptor expected_socket_descriptor_client = 23; + const std::string expected_address = "somehost:13579"; + const uint64_t expected_file_id = 314322; + const uint64_t expected_file_size = 784387; + const FileDescriptor expected_fd = 12643; + + Error err; + + ::testing::NiceMock<hidra2::MockIO> mock_io; + hidra2::Receiver receiver; + + void SetUp() override { + err = nullptr; + receiver.io__ = std::unique_ptr<hidra2::IO> {&mock_io}; + } + void TearDown() override { + receiver.io__.release(); + } + + +}; + +TEST_F(StartListenerFixture, CreateAndBindIPTCPSocketListenerError) { + EXPECT_CALL(mock_io, CreateAndBindIPTCPSocketListener_t(expected_address, receiver.kMaxUnacceptedConnectionsBacklog, _)) + .WillOnce(DoAll( + SetArgPointee<2>(hidra2::IOErrorTemplates::kUnknownIOError.Generate().release()), + Return(0) + )); + + receiver.Listen(expected_address, &err, true); + + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError)); +} + + +TEST_F(StartListenerFixture, InetAcceptConnectionError) { + EXPECT_CALL(mock_io, InetAcceptConnection_t(_, _)) + .WillOnce(DoAll( + SetArgPointee<1>(hidra2::IOErrorTemplates::kUnknownIOError.Generate().release()), + Return(new std::tuple<std::string, SocketDescriptor>(expected_address, expected_socket_descriptor_client)) + )); + + receiver.Listen(expected_address, &err, true); + + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError)); +} + +TEST_F(StartListenerFixture, Ok) { + + EXPECT_CALL(mock_io, InetAcceptConnection_t(_, _)) + .WillOnce(DoAll( + SetArgPointee<1>(nullptr), + Return(new std::tuple<std::string, SocketDescriptor>(expected_address, expected_socket_descriptor_client)) + )); + + EXPECT_CALL(mock_io, NewThread_t(_)). + WillOnce( + Return(nullptr) + ); + + receiver.Listen(expected_address, &err, true); + + ASSERT_THAT(err, Eq(nullptr)); +} + + +} diff --git a/receiver/unittests/test_request.cpp b/receiver/unittests/test_request.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b263826fe308534afd922688f6a64cf2311619e --- /dev/null +++ b/receiver/unittests/test_request.cpp @@ -0,0 +1,175 @@ +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <unittests/MockIO.h> +#include "../src/connection.h" +#include "../src/receiver_error.h" +#include "../src/request.h" +#include "../src/request_handler.h" +#include "../src/request_handler_file_write.h" + +using ::testing::Test; +using ::testing::Return; +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgReferee; +using ::testing::Gt; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::InSequence; +using ::testing::SetArgPointee; +using ::hidra2::Error; +using ::hidra2::ErrorInterface; +using ::hidra2::FileDescriptor; +using ::hidra2::SocketDescriptor; +using ::hidra2::GenericNetworkRequestHeader; +using ::hidra2::SendDataResponse; +using ::hidra2::GenericNetworkRequestHeader; +using ::hidra2::GenericNetworkResponse; +using ::hidra2::Opcode; +using ::hidra2::Connection; +using ::hidra2::MockIO; +using hidra2::Request; + +namespace { + +class MockReqestHandler : public hidra2::RequestHandler { + public: + Error ProcessRequest(const Request& request) const override { + return Error{ProcessRequest_t(request)}; + } + + MOCK_CONST_METHOD1(ProcessRequest_t, ErrorInterface * (const Request& request)); + +}; + +class FactoryTests : public Test { + public: + hidra2::RequestFactory factory; + Error err{nullptr}; + GenericNetworkRequestHeader generic_request_header; + void SetUp() override { + } + void TearDown() override { + } +}; + +TEST_F(FactoryTests, ErrorOnWrongCode) { + generic_request_header.op_code = hidra2::Opcode::kNetOpcodeUnknownOp; + auto request = factory.GenerateRequest(generic_request_header, 1, &err); + + ASSERT_THAT(err, Ne(nullptr)); +} + +TEST_F(FactoryTests, ReturnsDataRequestOnkNetOpcodeSendDataCode) { + generic_request_header.op_code = hidra2::Opcode::kNetOpcodeSendData; + auto request = factory.GenerateRequest(generic_request_header, 1, &err); + + ASSERT_THAT(err, Eq(nullptr)); + ASSERT_THAT(dynamic_cast<hidra2::Request*>(request.get()), Ne(nullptr)); + ASSERT_THAT(dynamic_cast<const hidra2::RequestHandlerFileWrite*>(request->GetListHandlers()[0]), Ne(nullptr)); +} + + + + +class RequestTests : public Test { + public: + GenericNetworkRequestHeader generic_request_header; + hidra2::SocketDescriptor socket_fd_{1}; + uint64_t data_size_ {100}; + uint64_t data_id_{15}; + std::unique_ptr<Request> request; + NiceMock<MockIO> mock_io; + void SetUp() override { + generic_request_header.data_size = data_size_; + generic_request_header.data_id = data_id_; + request.reset(new Request{generic_request_header, socket_fd_}); + request->io__ = std::unique_ptr<hidra2::IO> {&mock_io}; + ON_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillByDefault( + DoAll(SetArgPointee<3>(nullptr), + Return(0) + )); + } + void TearDown() override { + request->io__.release(); + } + +}; + +TEST_F(RequestTests, HandleDoesNotReceiveEmptyData) { + generic_request_header.data_size = 0; + request->io__.release(); + request.reset(new Request{generic_request_header, socket_fd_}); + request->io__ = std::unique_ptr<hidra2::IO> {&mock_io};; + + EXPECT_CALL(mock_io, Receive_t(_, _, _, _)).Times(0); + + auto err = request->Handle(); + + ASSERT_THAT(err, Eq(nullptr)); +} + +TEST_F(RequestTests, HandleReturnsErrorOnDataReceive) { + EXPECT_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillOnce( + DoAll(SetArgPointee<3>(new hidra2::IOError("Test Read Error", hidra2::IOErrorType::kReadError)), + Return(0) + )); + + auto err = request->Handle(); + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kReadError)); +} + + +TEST_F(RequestTests, HandleProcessesRequests) { + + MockReqestHandler mock_request_handler; + + EXPECT_CALL(mock_request_handler, ProcessRequest_t(_)).WillOnce( + Return(nullptr) + ).WillOnce( + Return(new hidra2::IOError("Test Send Error", hidra2::IOErrorType::kUnknownIOError)) + );; + + request->AddHandler(&mock_request_handler); + request->AddHandler(&mock_request_handler); + + auto err = request->Handle(); + + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError)); +} + +TEST_F(RequestTests, DataIsNullAtInit) { + auto& data = request->GetData(); + + ASSERT_THAT(data, Eq(nullptr)); +} + +TEST_F(RequestTests, GetDataIsNotNullptr) { + + request->Handle(); + auto& data = request->GetData(); + + + ASSERT_THAT(data, Ne(nullptr)); + + +} + + +TEST_F(RequestTests, GetDataSize) { + auto size = request->GetDataSize(); + + ASSERT_THAT(size, Eq(data_size_)); +} + +TEST_F(RequestTests, GetFileName) { + auto fname = request->GetFileName(); + auto s = std::to_string(data_id_) + ".bin"; + + ASSERT_THAT(fname, Eq(s)); +} + + +} diff --git a/receiver/unittests/test_request_handler_file_write.cpp b/receiver/unittests/test_request_handler_file_write.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7c3dc1d09a1f0cc8396cd961e1c5ac7de93a986d --- /dev/null +++ b/receiver/unittests/test_request_handler_file_write.cpp @@ -0,0 +1,113 @@ +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <unittests/MockIO.h> +#include "../src/receiver_error.h" +#include "../src/request.h" +#include "../src/request_handler.h" +#include "../src/request_handler_file_write.h" +#include "common/networking.h" + +using ::testing::Test; +using ::testing::Return; +using ::testing::ReturnRef; +using ::testing::_; +using ::testing::DoAll; +using ::testing::SetArgReferee; +using ::testing::Gt; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::InSequence; +using ::testing::SetArgPointee; +using ::hidra2::Error; +using ::hidra2::ErrorInterface; +using ::hidra2::FileDescriptor; +using ::hidra2::SocketDescriptor; +using ::hidra2::MockIO; +using hidra2::Request; +using hidra2::RequestHandlerFileWrite; +using ::hidra2::GenericNetworkRequestHeader; + +namespace { + +class MockRequest: public Request { + public: + MockRequest(const GenericNetworkRequestHeader& request_header, SocketDescriptor socket_fd): + Request(request_header, socket_fd) {}; + + MOCK_CONST_METHOD0(GetFileName, std::string()); + MOCK_CONST_METHOD0(GetDataSize, uint64_t()); + MOCK_CONST_METHOD0(GetData, const hidra2::FileData & ()); +}; + +class FileWriteHandlerTests : public Test { + public: + RequestHandlerFileWrite handler; + NiceMock<MockIO> mock_io; + std::unique_ptr<MockRequest> mock_request; + void SetUp() override { + GenericNetworkRequestHeader request_header; + request_header.data_id = 2; + mock_request.reset(new MockRequest{request_header, 1}); + handler.io__ = std::unique_ptr<hidra2::IO> {&mock_io}; + /* ON_CALL(mock_io, Receive_t(socket_fd_, _, data_size_, _)).WillByDefault( + DoAll(SetArgPointee<3>(nullptr), + Return(0) + ));*/ + } + void TearDown() override { + handler.io__.release(); + } + +}; + +TEST_F(FileWriteHandlerTests, ErrorWhenZeroFileSize) { + EXPECT_CALL(*mock_request, GetDataSize()) + .WillOnce(Return(0)) + ; + + auto err = handler.ProcessRequest(*mock_request); + + ASSERT_THAT(err, Eq(hidra2::ReceiverErrorTemplates::kBadRequest)); +} + +TEST_F(FileWriteHandlerTests, ErrorWhenTooBigFileSize) { + EXPECT_CALL(*mock_request, GetDataSize()) + .WillOnce(Return(hidra2::kMaxFileSize + 1)) + ; + + auto err = handler.ProcessRequest(*mock_request); + + ASSERT_THAT(err, Eq(hidra2::ReceiverErrorTemplates::kBadRequest)); +} + +TEST_F(FileWriteHandlerTests, CallsWriteFile) { + std::string expected_file_name = "2.bin"; + uint64_t expected_file_size = 10; + EXPECT_CALL(*mock_request, GetDataSize()) + .WillOnce(Return(expected_file_size)) + ; + + hidra2::FileData data; + EXPECT_CALL(*mock_request, GetData()) + .WillOnce(ReturnRef(data)) + ; + + EXPECT_CALL(*mock_request, GetFileName()) + .WillOnce(Return(expected_file_name)) + ; + + + EXPECT_CALL(mock_io, WriteDataToFile_t("files/" + expected_file_name, _, expected_file_size)) + .WillOnce( + Return(hidra2::IOErrorTemplates::kUnknownIOError.Generate().release()) + ); + + auto err = handler.ProcessRequest(*mock_request); + + ASSERT_THAT(err, Eq(hidra2::IOErrorTemplates::kUnknownIOError)); +} + + +} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 47f40c7bca39f70f88fe1b8a0a8d38e46fecc111..3e431d34cd6ac7e59e99566ff0151bea49a95dbf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(automatic) + diff --git a/tests/automatic/CMakeLists.txt b/tests/automatic/CMakeLists.txt index 0fb7141827c5997da8294a401a2bb2a3721a238e..1c613b8555c478cc08aacf1db994443192bbe1b5 100644 --- a/tests/automatic/CMakeLists.txt +++ b/tests/automatic/CMakeLists.txt @@ -19,3 +19,4 @@ endif() add_subdirectory(worker) +add_subdirectory(producer_receiver) \ No newline at end of file diff --git a/tests/automatic/common/cpp/CMakeLists.txt b/tests/automatic/common/cpp/CMakeLists.txt index 1eec82d9fb06edce8549838ed1a56c15b5a20f45..520d0153db6a34e18e9aeaf68448af3dfe6c0b5e 100644 --- a/tests/automatic/common/cpp/CMakeLists.txt +++ b/tests/automatic/common/cpp/CMakeLists.txt @@ -8,3 +8,7 @@ set(SOURCE_FILES add_library(${TARGET_NAME} STATIC ${SOURCE_FILES}) target_include_directories(${TARGET_NAME} PUBLIC include) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES}) diff --git a/tests/automatic/producer_receiver/CMakeLists.txt b/tests/automatic/producer_receiver/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4da20d000bc7375e799fcd9b063a640d75c506a --- /dev/null +++ b/tests/automatic/producer_receiver/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(transfer_single_file) diff --git a/tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt b/tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..83a9f59580c5427848720b28aea7353d86335d23 --- /dev/null +++ b/tests/automatic/producer_receiver/transfer_single_file/CMakeLists.txt @@ -0,0 +1,6 @@ +set(TARGET_NAME transfer-single-file) + +################################ +# Testing +################################ +add_script_test("${TARGET_NAME}" "$<TARGET_FILE:dummy-data-producer> $<TARGET_FILE:receiver-bin>" nomem) diff --git a/tests/automatic/producer_receiver/transfer_single_file/check_linux.sh b/tests/automatic/producer_receiver/transfer_single_file/check_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..163d4ab08b8144ea133ca0f6f48899b304234e63 --- /dev/null +++ b/tests/automatic/producer_receiver/transfer_single_file/check_linux.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +trap Cleanup EXIT + +Cleanup() { + echo cleanup + kill $receiverid + rm -rf files +} + +nohup $2 &>/dev/null & +sleep 0.3 +receiverid=`echo $!` + +mkdir files + +$1 localhost:4200 100 1 + +ls -ln files/0.bin | awk '{ print $5 }'| grep 100 diff --git a/tests/automatic/producer_receiver/transfer_single_file/check_windows.bat b/tests/automatic/producer_receiver/transfer_single_file/check_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..9413a82a228200e5a50ed87135e10450ab471714 --- /dev/null +++ b/tests/automatic/producer_receiver/transfer_single_file/check_windows.bat @@ -0,0 +1,28 @@ +set full_recv_name="%2" +set short_recv_name="%~nx2" + +start /B "" "%full_recv_name%" + +ping 1.0.0.0 -n 1 -w 100 > nul + +mkdir files + +%1 localhost:4200 100 1 + +ping 1.0.0.0 -n 1 -w 100 > nul + +FOR /F "usebackq" %%A IN ('files\0.bin') DO set size=%%~zA + +if %size% NEQ 100 goto :error + +goto :clean + +:error +call :clean +exit /b 1 + +:clean +Taskkill /IM "%short_recv_name%" /F +rmdir /S /Q files + + diff --git a/tests/automatic/system_io/CMakeLists.txt b/tests/automatic/system_io/CMakeLists.txt index 60be4b4c07fa5d1f9e8224a79a4f6a847c0d5600..84f9db59c371ee52ea4dc29e0a56f25e18a6cba2 100644 --- a/tests/automatic/system_io/CMakeLists.txt +++ b/tests/automatic/system_io/CMakeLists.txt @@ -3,4 +3,6 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.7) # needed for fixtures add_subdirectory(read_folder_content) add_subdirectory(read_file_content) add_subdirectory(read_string_from_file) - +add_subdirectory(ip_tcp_network) +add_subdirectory(resolve_hostname_to_ip) +add_subdirectory(write_data_to_file) diff --git a/tests/automatic/system_io/ip_tcp_network/CMakeLists.txt b/tests/automatic/system_io/ip_tcp_network/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cbe323ad4a425bdbbc7431e1c22198616034098d --- /dev/null +++ b/tests/automatic/system_io/ip_tcp_network/CMakeLists.txt @@ -0,0 +1,16 @@ +set(TARGET_NAME ip_tcp_network) +set(SOURCE_FILES ip_tcp_network.cpp) + +################################ +# Executable and link +################################ +add_executable(${TARGET_NAME} ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> ) +target_link_libraries(${TARGET_NAME} test_common) +target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + +################################ +# Testing +################################ +# memory test too slow +add_integration_test(${TARGET_NAME} ip_tcp_network " " nomem) diff --git a/tests/automatic/system_io/ip_tcp_network/ip_tcp_network.cpp b/tests/automatic/system_io/ip_tcp_network/ip_tcp_network.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a38a459eb1d69e128f3e2a9ee0067f7bcc0e9e94 --- /dev/null +++ b/tests/automatic/system_io/ip_tcp_network/ip_tcp_network.cpp @@ -0,0 +1,183 @@ +#include <iostream> +#include "io/io_factory.h" +#include <chrono> +#include <thread> +#include <future> + +#include "testing.h" + +using hidra2::Error; +using hidra2::ErrorType; +using hidra2::AddressFamilies; +using hidra2::SocketTypes; +using hidra2::SocketProtocols; +using hidra2::FileDescriptor; +using hidra2::M_AssertEq; + +using namespace std::chrono; + +static const std::unique_ptr<hidra2::IO> io(hidra2::GenerateDefaultIO()); +static const std::string kListenAddress = "127.0.0.1:60123"; +static std::promise<void> kThreadStarted; +static const int kNumberOfChecks = 2; +static const size_t kSkipAmount = 500; + +void Exit(int exit_number) { + std::cerr << "ERROR: Exit on " << exit_number << std::endl; + exit(exit_number); +} + +void ExitIfErrIsNotOk(Error* err, int exit_number) { + if(*err != nullptr) { + std::cerr << "Explain(): " << (*err)->Explain() << std::endl; + Exit(exit_number); + } +} + +std::unique_ptr<std::thread> CreateEchoServerThread() { + return io->NewThread([&] { + Error err; + FileDescriptor socket = io->CreateSocket(AddressFamilies::INET, SocketTypes::STREAM, SocketProtocols::IP, &err); + ExitIfErrIsNotOk(&err, 100); + io->InetBind(socket, kListenAddress, &err); + std::cout << "[SERVER] Listen" << std::endl; + ExitIfErrIsNotOk(&err, 101); + io->Listen(socket, 5, &err); + ExitIfErrIsNotOk(&err, 102); + kThreadStarted.set_value(); + + for(int i = 0; i < kNumberOfChecks; i++) { + std::cout << "[SERVER][" << i << "] InetAcceptConnection" << std::endl; + auto client_info_tuple = io->InetAcceptConnection(socket, &err); + ExitIfErrIsNotOk(&err, 103); + std::string client_address; + FileDescriptor client_fd; + std::tie(client_address, client_fd) = *client_info_tuple; + + ExitIfErrIsNotOk(&err, 104); + while (true) { + uint64_t need_to_receive_size; + io->ReceiveWithTimeout(client_fd, &need_to_receive_size, sizeof(uint64_t), 100, &err); + if(err != nullptr) { + if (hidra2::IOErrorTemplates::kTimeout == err) { + continue; + } + if (hidra2::ErrorTemplates::kEndOfFile == err) { + break; + } + } + ExitIfErrIsNotOk(&err, 105);//ReceiveWithTimeout + + std::unique_ptr<uint8_t[]> buffer(new uint8_t[need_to_receive_size]); + + io->Skip(client_fd, kSkipAmount, &err); + ExitIfErrIsNotOk(&err, 106); + + size_t received = io->Receive(client_fd, buffer.get(), need_to_receive_size, &err); + io->Send(client_fd, buffer.get(), received, &err); + ExitIfErrIsNotOk(&err, 107); + } + + std::cout << "[SERVER][" << i << "] Close client_fd" << std::endl; + io->CloseSocket(client_fd, &err); + ExitIfErrIsNotOk(&err, 108); + } + std::cout << "[SERVER] Close server socket" << std::endl; + io->CloseSocket(socket, &err); + ExitIfErrIsNotOk(&err, 109); + }); +} + +void CheckNormal(int times, size_t size) { + Error err; + std::cout << "[CLIENT] CreateAndConnectIPTCPSocket" << std::endl; + FileDescriptor socket = io->CreateAndConnectIPTCPSocket(kListenAddress, &err); + ExitIfErrIsNotOk(&err, 201); + + std::cout << "[CLIENT] ReceiveWithTimeout" << std::endl; + io->ReceiveWithTimeout(socket, nullptr, 1, 1000 * 100/*100ms*/, &err); + if (hidra2::IOErrorTemplates::kTimeout != err) { + ExitIfErrIsNotOk(&err, 202); + } + + for (int i = 0; i < times; i++) { + std::cout << "[CLIENT] Allocate and create random numbers" << std::endl; + std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]); + for (size_t i = 0; i < size; i++) { + buffer[i] = rand(); + } + + uint64_t send_size = size; + + std::cout << "[CLIENT] Send Size" << std::endl; + io->Send(socket, &send_size, sizeof(uint64_t), &err); + ExitIfErrIsNotOk(&err, 203); + + std::cout << "[CLIENT] Send data to skip" << std::endl; + io->Send(socket, buffer.get(), kSkipAmount, &err); + ExitIfErrIsNotOk(&err, 204); + + std::cout << "[CLIENT] Send" << std::endl; + io->Send(socket, buffer.get(), size, &err); + ExitIfErrIsNotOk(&err, 205); + + std::unique_ptr<uint8_t[]> buffer2(new uint8_t[size]); + std::cout << "[CLIENT] Receive" << std::endl; + size_t receive_count = io->Receive(socket, buffer2.get(), size, &err); + ExitIfErrIsNotOk(&err, 206); + if(receive_count != size) { + Exit(205); + } + + std::cout << "[CLIENT] buffer check" << std::endl; + for (size_t i = 0; i < size; i++) { + if (buffer[i] != buffer2[i]) { + Exit(207); + } + } + } + + std::cout << "[CLIENT] Close" << std::endl; + io->CloseSocket(socket, &err); + ExitIfErrIsNotOk(&err, 108); +} + +int main(int argc, char* argv[]) { + Error err; + std::cout << "[META] Check if connection is refused if server is not running" << std::endl; + io->CreateAndConnectIPTCPSocket(kListenAddress, &err); + if(hidra2::IOErrorTemplates::kConnectionRefused != err) { + ExitIfErrIsNotOk(&err, 301); + } + + std::cout << "[META] Check invalid address format - Missing port" << std::endl; + io->CreateAndConnectIPTCPSocket("localhost", &err); + if(hidra2::IOErrorTemplates::kInvalidAddressFormat != err) { + ExitIfErrIsNotOk(&err, 302); + } + + std::cout << "[META] Check unknown host" << std::endl; + io->CreateAndConnectIPTCPSocket("some-host-that-might-not-exists.aa:1234", &err); + if(hidra2::IOErrorTemplates::kUnableToResolveHostname != err) { + ExitIfErrIsNotOk(&err, 303); + } + + std::unique_ptr<std::thread> server_thread = CreateEchoServerThread(); + kThreadStarted.get_future().get();//Make sure that the server is started + + std::cout << "Check 1" << std::endl; + CheckNormal(10, 1024 * 1024 * 3);//3 MiByte + std::cout << "Check 2" << std::endl; + CheckNormal(30, 1024);//1 KiByte + + std::cout << "server_thread->join()" << std::endl; + server_thread->join(); + + std::cout << "[META] Check if connection is refused after server is closed" << std::endl; + io->CreateAndConnectIPTCPSocket(kListenAddress, &err); + if(hidra2::IOErrorTemplates::kConnectionRefused != err) { + ExitIfErrIsNotOk(&err, 304); + } + + return 0; +} diff --git a/tests/automatic/system_io/read_file_content/CMakeLists.txt b/tests/automatic/system_io/read_file_content/CMakeLists.txt index 253fc7d58fbd0255f5d12b38c8374a2b6810570c..c65eb453715599a2237d6097832c420af90bbad0 100644 --- a/tests/automatic/system_io/read_file_content/CMakeLists.txt +++ b/tests/automatic/system_io/read_file_content/CMakeLists.txt @@ -7,7 +7,7 @@ set(SOURCE_FILES read_file_content.cpp) ################################ add_executable(${TARGET_NAME} ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> ) target_link_libraries(${TARGET_NAME} test_common) -target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) +target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/cpp/include) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) ################################ diff --git a/tests/automatic/system_io/read_file_content/read_file_content.cpp b/tests/automatic/system_io/read_file_content/read_file_content.cpp index 065e94328e4523ff40b341ec42de6e3844ad1365..a8dc00ed101e71c29634fd36bfcc265d28836c4f 100644 --- a/tests/automatic/system_io/read_file_content/read_file_content.cpp +++ b/tests/automatic/system_io/read_file_content/read_file_content.cpp @@ -1,9 +1,8 @@ #include <iostream> -#include <system_wrappers/system_io.h> +#include "io/io_factory.h" #include "testing.h" -using hidra2::SystemIO; int main(int argc, char* argv[]) { if (argc != 3) { @@ -13,7 +12,8 @@ int main(int argc, char* argv[]) { std::string expect{argv[2]}; hidra2::Error err; - auto io = std::unique_ptr<SystemIO> {new SystemIO}; + auto io = std::unique_ptr<hidra2::IO> {hidra2::GenerateDefaultIO()}; + auto data = io->GetDataFromFile(argv[1], expect.size(), &err); std::string result; diff --git a/tests/automatic/system_io/read_folder_content/CMakeLists.txt b/tests/automatic/system_io/read_folder_content/CMakeLists.txt index bf53584f05643144f8a8c2f3a976428347fa11d5..318db28cd3fbcb9702fd532427321134c6820dbe 100644 --- a/tests/automatic/system_io/read_folder_content/CMakeLists.txt +++ b/tests/automatic/system_io/read_folder_content/CMakeLists.txt @@ -7,6 +7,11 @@ set(SOURCE_FILES read_folder_content.cpp) ################################ add_executable(${TARGET_NAME} ${SOURCE_FILES} $<TARGET_OBJECTS:system_io>) target_link_libraries(${TARGET_NAME} test_common) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES}) + target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) diff --git a/tests/automatic/system_io/read_folder_content/read_folder_content.cpp b/tests/automatic/system_io/read_folder_content/read_folder_content.cpp index 9e6bbbd3ef4f16ad9d7819d9e1b54441650e4299..11ce72c2525f6b48e2a6b31ee613d81723b8c7d3 100644 --- a/tests/automatic/system_io/read_folder_content/read_folder_content.cpp +++ b/tests/automatic/system_io/read_folder_content/read_folder_content.cpp @@ -1,9 +1,9 @@ #include <iostream> -#include "system_wrappers/system_io.h" +#include "io/io_factory.h" #include "testing.h" -using hidra2::SystemIO; +using hidra2::IO; using hidra2::Error; @@ -18,7 +18,7 @@ int main(int argc, char* argv[]) { std::string expect{argv[2]}; Error err; - auto io = std::unique_ptr<SystemIO> {new SystemIO}; + auto io = std::unique_ptr<IO> {hidra2::GenerateDefaultIO() }; auto files = io->FilesInFolder(argv[1], &err); std::string result{}; diff --git a/tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp b/tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp index 6a41e2e76ec4f39322c9f3118d0f0b620dbd412d..3248c67e5eeb2eeee7648fb06f9d9625dbe05891 100644 --- a/tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp +++ b/tests/automatic/system_io/read_string_from_file/read_string_from_file.cpp @@ -1,10 +1,8 @@ #include <iostream> -#include <system_wrappers/system_io.h> +#include "io/io_factory.h" #include "testing.h" -using hidra2::SystemIO; - int main(int argc, char* argv[]) { if (argc != 3) { std::cout << "Wrong number of arguments" << std::endl; @@ -13,7 +11,7 @@ int main(int argc, char* argv[]) { std::string expect{argv[2]}; hidra2::Error err; - auto io = std::unique_ptr<SystemIO> {new SystemIO}; + auto io = std::unique_ptr<hidra2::IO> {hidra2::GenerateDefaultIO()}; auto str = io->ReadFileToString(argv[1], &err); std::string result; diff --git a/tests/automatic/system_io/resolve_hostname_to_ip/CMakeLists.txt b/tests/automatic/system_io/resolve_hostname_to_ip/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..7bc06ca1db15e1ba4234b02d01538c5132d1474b --- /dev/null +++ b/tests/automatic/system_io/resolve_hostname_to_ip/CMakeLists.txt @@ -0,0 +1,16 @@ +set(TARGET_NAME resolve_hostname_to_ip) +set(SOURCE_FILES resolve_hostname_to_ip.cpp) + +################################ +# Executable and link +################################ +add_executable(${TARGET_NAME} ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> ) +target_link_libraries(${TARGET_NAME} test_common) +target_include_directories(${TARGET_NAME} PUBLIC ${HIDRA2_CXX_COMMON_INCLUDE_DIR}) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + +################################ +# Testing +################################ + +add_integration_test(${TARGET_NAME} resolve_hostname_to_ip "") diff --git a/tests/automatic/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp b/tests/automatic/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a5a3757821a80e3b442df46d7315987fd980b06f --- /dev/null +++ b/tests/automatic/system_io/resolve_hostname_to_ip/resolve_hostname_to_ip.cpp @@ -0,0 +1,38 @@ +#include <iostream> +#include "io/io_factory.h" + +#include "testing.h" + +using hidra2::Error; +using hidra2::ErrorType; + +using hidra2::M_AssertEq; +using hidra2::M_AssertTrue; + + +void Check(const std::string& expected_ip_address, const std::string& hostname) { + std::cout << "Checking: " << hostname << std::endl; + Error err; + auto io = std::unique_ptr<hidra2::IO> {hidra2::GenerateDefaultIO()}; + std::string ip_address = io->ResolveHostnameToIp(hostname, &err); + M_AssertEq(expected_ip_address, ip_address); + if(expected_ip_address.empty()) { + M_AssertTrue(err != nullptr && hidra2::IOErrorTemplates::kUnableToResolveHostname == err); + return; + } + M_AssertTrue(err == nullptr); +} + +int main(int argc, char* argv[]) { + Check("127.0.0.1", "localhost"); + Check("8.8.8.8", "google-public-dns-a.google.com"); + Check("8.8.4.4", "google-public-dns-b.google.com"); + Check("4.2.2.1", "a.resolvers.level3.net"); + Check("4.2.2.2", "b.resolvers.level3.net"); + Check("4.2.2.3", "c.resolvers.level3.net"); + Check("4.2.2.4", "d.resolvers.level3.net"); + + Check("", "some-address-that-does-not-exists.ff"); + Check("", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.ff"); + return 0; +} diff --git a/tests/automatic/system_io/write_data_to_file/CMakeLists.txt b/tests/automatic/system_io/write_data_to_file/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fd034a4d2385b6b8069962b68ffc0485dd16afaa --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/CMakeLists.txt @@ -0,0 +1,20 @@ +set(TARGET_NAME write_data_to_file) +set(SOURCE_FILES write_data_to_file.cpp) + + +################################ +# Executable and link +################################ +add_executable(${TARGET_NAME} ${SOURCE_FILES} $<TARGET_OBJECTS:system_io> ) +target_link_libraries(${TARGET_NAME} test_common) +target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/common/cpp/include) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) + +################################ +# Testing +################################ +add_test_setup_cleanup(${TARGET_NAME}) +add_integration_test(${TARGET_NAME} writeok "test_file ok dummy" nomem) +add_integration_test(${TARGET_NAME} writetwice "test_file error kFileAlreadyExists:test" nomem) +add_integration_test(${TARGET_NAME} dirnoaccess "test_noaccess/test_file error Permissiondenied:test_noaccess/test_file" nomem) + diff --git a/tests/automatic/system_io/write_data_to_file/cleanup_linux.sh b/tests/automatic/system_io/write_data_to_file/cleanup_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..1a50a82324730dc9630f63142d6328926b2ca9f4 --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/cleanup_linux.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +rm -f test_file +rmdir test_noaccess + diff --git a/tests/automatic/system_io/write_data_to_file/cleanup_windows.bat b/tests/automatic/system_io/write_data_to_file/cleanup_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..256da63ada32b6f56a0273cc6ea378675be066cf --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/cleanup_windows.bat @@ -0,0 +1,4 @@ +del test_file + +icacls test_noaccess /grant:r users:W +rmdir /S /Q test_noaccess diff --git a/tests/automatic/system_io/write_data_to_file/setup_linux.sh b/tests/automatic/system_io/write_data_to_file/setup_linux.sh new file mode 100644 index 0000000000000000000000000000000000000000..63356273ee156761c7bcbcf4a9524d32cf134416 --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/setup_linux.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +mkdir test_noaccess +chmod -rx test_noaccess + diff --git a/tests/automatic/system_io/write_data_to_file/setup_windows.bat b/tests/automatic/system_io/write_data_to_file/setup_windows.bat new file mode 100644 index 0000000000000000000000000000000000000000..bc36d4ed8d9c3ac8e998f8ea7970ce8e1b1bb89d --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/setup_windows.bat @@ -0,0 +1,5 @@ +mkdir test_noaccess +icacls test_noaccess /deny users:W + + + diff --git a/tests/automatic/system_io/write_data_to_file/write_data_to_file.cpp b/tests/automatic/system_io/write_data_to_file/write_data_to_file.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3abfd66d2b90537fcfe8f189918e2a23cdae02b3 --- /dev/null +++ b/tests/automatic/system_io/write_data_to_file/write_data_to_file.cpp @@ -0,0 +1,65 @@ +#include <iostream> +#include "io/io_factory.h" + +#include "testing.h" + +using hidra2::IO; +using hidra2::Error; +using hidra2::FileData; + + +struct Params { + std::string fname; + std::string result; + std::string message; + int length; +}; + +Params GetParams(int argc, char* argv[]) { + if (argc != 4) { + std::cout << "Wrong number of arguments" << std::endl; + exit(EXIT_FAILURE); + } + std::string fname{argv[1]}; + std::string result{argv[2]}; + std::string message{argv[3]}; + + return Params{fname, result, message, 3}; +} + +void AssertGoodResult(const std::unique_ptr<IO>& io, const Error& err, const FileData& data, + const Params& params) { + if (err) { + std::cerr << err << std::endl; + exit(EXIT_FAILURE); + } + Error read_err; + auto read_data = io->GetDataFromFile(params.fname, params.length, &read_err); + hidra2::M_AssertContains(std::string(read_data.get(), read_data.get() + params.length), "123"); +} + +void AssertBadResult(const Error& err, const Params& params) { + if (err == nullptr) { + std::cerr << "Should be error" << std::endl; + exit(EXIT_FAILURE); + } + hidra2::M_AssertContains(err->Explain(), params.message); +} + + +int main(int argc, char* argv[]) { + auto params = GetParams(argc, argv); + + auto io = std::unique_ptr<hidra2::IO> {hidra2::GenerateDefaultIO()}; + FileData data{new uint8_t[params.length]{'1', '2', '3'}}; + + auto err = io->WriteDataToFile(params.fname, data, params.length); + + if (params.result == "ok") { + AssertGoodResult(io, err, data, params); + } else { + AssertBadResult(err, params); + } + + return 0; +} diff --git a/tests/automatic/worker/connect_multithread/CMakeLists.txt b/tests/automatic/worker/connect_multithread/CMakeLists.txt index 31364d77e74b1b833bc6d06d63105fbdf967f9f6..abe83367dc6b2796d7ca4d734b6ed4d37b8ed167 100644 --- a/tests/automatic/worker/connect_multithread/CMakeLists.txt +++ b/tests/automatic/worker/connect_multithread/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCE_FILES content_multithread.cpp) # Executable and link ################################ add_executable(${TARGET_NAME} ${SOURCE_FILES}) -target_link_libraries(${TARGET_NAME} test_common hidra2-worker ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${TARGET_NAME} test_common hidra2-worker ${HIDRA2_COMMON_IO_LIBRARIES}) set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) if (CMAKE_COMPILER_IS_GNUCXX) set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage") diff --git a/tests/automatic/worker/curl_http_client_get/curl_httpclient_get.cpp b/tests/automatic/worker/curl_http_client_get/curl_httpclient_get.cpp index 057c474f1445e4b02a95e6480eafe5b39d9ae745..478839271d364ce2bcfa98cc00de5ac4f602707d 100644 --- a/tests/automatic/worker/curl_http_client_get/curl_httpclient_get.cpp +++ b/tests/automatic/worker/curl_http_client_get/curl_httpclient_get.cpp @@ -34,15 +34,15 @@ int main(int argc, char* argv[]) { auto server_broker = static_cast<hidra2::ServerDataBroker*>(broker.get()); hidra2::HttpCode code; - auto responce = server_broker->httpclient__->Get(args.uri, &code, &err); + auto response = server_broker->httpclient__->Get(args.uri, &code, &err); if (err != nullptr) { M_AssertEq("clienterror", args.answer); - M_AssertContains(responce, "Could"); + M_AssertContains(response, "Could"); return 0; } - M_AssertContains(responce, args.answer); + M_AssertContains(response, args.answer); M_AssertEq(static_cast<int>(code), args.code); return 0; diff --git a/tests/automatic/worker/next_multithread_folder/CMakeLists.txt b/tests/automatic/worker/next_multithread_folder/CMakeLists.txt index 685e69ffc1d84c130f0acf98f264d7096bd6c15d..eed3cc9b92840d9f2fe0aebda644df863acb553a 100644 --- a/tests/automatic/worker/next_multithread_folder/CMakeLists.txt +++ b/tests/automatic/worker/next_multithread_folder/CMakeLists.txt @@ -6,7 +6,17 @@ set(SOURCE_FILES next_multithread_folder.cpp) # Executable and link ################################ add_executable(${TARGET_NAME} ${SOURCE_FILES}) -target_link_libraries(${TARGET_NAME} test_common hidra2-worker ${CMAKE_THREAD_LIBS_INIT}) + +#Add all necessary common libraries +GET_PROPERTY(HIDRA2_COMMON_IO_LIBRARIES GLOBAL PROPERTY HIDRA2_COMMON_IO_LIBRARIES) +target_link_libraries(${TARGET_NAME} ${HIDRA2_COMMON_IO_LIBRARIES}) + +target_link_libraries(${TARGET_NAME} test_common hidra2-worker) +set_target_properties(${TARGET_NAME} PROPERTIES LINKER_LANGUAGE CXX) +if (CMAKE_COMPILER_IS_GNUCXX) + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS_DEBUG "--coverage") +endif() + ################################ # Testing diff --git a/worker/api/cpp/src/curl_http_client.cpp b/worker/api/cpp/src/curl_http_client.cpp index 4cec61f6875159372afdc316b90d018baf8da52f..364b87cd1975a90de5ec53cc3eefaeb8e126265c 100644 --- a/worker/api/cpp/src/curl_http_client.cpp +++ b/worker/api/cpp/src/curl_http_client.cpp @@ -35,7 +35,7 @@ void SetCurlOptions(CURL* curl, const std::string& uri, char* errbuf, std::strin } -HttpCode GetResponceCode(CURL* curl) { +HttpCode GetResponseCode(CURL* curl) { long http_code = 0; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); return static_cast<HttpCode>(http_code); @@ -49,10 +49,10 @@ std::string GetCurlError(CURL* curl, CURLcode res, const char* errbuf) { } } -Error ProcessCurlResponce(CURL* curl, CURLcode res, const char* errbuf, - std::string* buffer, HttpCode* responce_code) { +Error ProcessCurlResponse(CURL* curl, CURLcode res, const char* errbuf, + std::string* buffer, HttpCode* response_code) { if(res == CURLE_OK) { - *responce_code = GetResponceCode(curl); + *response_code = GetResponseCode(curl); return nullptr; } else { *buffer = GetCurlError(curl, res, errbuf); @@ -60,7 +60,7 @@ Error ProcessCurlResponce(CURL* curl, CURLcode res, const char* errbuf, } } -std::string CurlHttpClient::Get(const std::string& uri, HttpCode* responce_code, Error* err) const noexcept { +std::string CurlHttpClient::Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept { std::lock_guard<std::mutex> lock{mutex_}; std::string buffer; @@ -69,7 +69,7 @@ std::string CurlHttpClient::Get(const std::string& uri, HttpCode* responce_code, auto res = curl_easy_perform(curl_); - *err = ProcessCurlResponce(curl_, res, errbuf, &buffer, responce_code); + *err = ProcessCurlResponse(curl_, res, errbuf, &buffer, response_code); return buffer; } diff --git a/worker/api/cpp/src/curl_http_client.h b/worker/api/cpp/src/curl_http_client.h index 578c033370a26f36f99a94bd665b3147fd4d342a..135a6cd5c5b38af9e3d54421538a2197ee9b0889 100644 --- a/worker/api/cpp/src/curl_http_client.h +++ b/worker/api/cpp/src/curl_http_client.h @@ -12,7 +12,7 @@ namespace hidra2 { class CurlHttpClient final : public HttpClient { public: CurlHttpClient(); - std::string Get(const std::string& uri, HttpCode* responce_code, Error* err) const noexcept override; + std::string Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept override; virtual ~CurlHttpClient(); private: mutable std::mutex mutex_; diff --git a/worker/api/cpp/src/folder_data_broker.cpp b/worker/api/cpp/src/folder_data_broker.cpp index f312536460ac664c815cc71200498dfa6f311b67..f15b3d6a260a80c8c14e3de635046c1654af077a 100644 --- a/worker/api/cpp/src/folder_data_broker.cpp +++ b/worker/api/cpp/src/folder_data_broker.cpp @@ -1,12 +1,12 @@ #include "folder_data_broker.h" -#include "system_wrappers/system_io.h" +#include "io/io_factory.h" namespace hidra2 { FolderDataBroker::FolderDataBroker(const std::string& source_name) : - io__{new hidra2::SystemIO}, base_path_{source_name}, is_connected_{false}, -current_file_{ -1} { + io__{GenerateDefaultIO()}, base_path_{source_name}, is_connected_{false}, + current_file_{ -1} { } Error FolderDataBroker::Connect() { @@ -37,7 +37,7 @@ Error FolderDataBroker::CanGetData(FileInfo* info, FileData* data, int nfile) co } if (nfile >= (int) filelist_.size()) { - return Error{TextErrorWithType(WorkerErrorMessage::kNoData, ErrorType::kEOF)}; + return Error{TextErrorWithType(WorkerErrorMessage::kNoData, ErrorType::kEndOfFile)}; } return nullptr; } @@ -67,4 +67,4 @@ Error FolderDataBroker::GetNext(FileInfo* info, FileData* data) { } -} \ No newline at end of file +} diff --git a/worker/api/cpp/src/folder_data_broker.h b/worker/api/cpp/src/folder_data_broker.h index 4ffe1b21177d770253bda50aae9c06567f5ad93b..f388e5b1fd726b34eae9035670b035ce1695af39 100644 --- a/worker/api/cpp/src/folder_data_broker.h +++ b/worker/api/cpp/src/folder_data_broker.h @@ -6,7 +6,7 @@ #include <string> #include <mutex> -#include "system_wrappers/io.h" +#include "io/io.h" namespace hidra2 { diff --git a/worker/api/cpp/src/http_client.cpp b/worker/api/cpp/src/http_client.cpp index acfec717cbe1546e7f696c9a441c087933e4275c..d5c86c6c07343e163714d36a2b22053138bec407 100644 --- a/worker/api/cpp/src/http_client.cpp +++ b/worker/api/cpp/src/http_client.cpp @@ -15,7 +15,7 @@ Error HttpCodeToWorkerError(const HttpCode& code) { break; case HttpCode::NoContent: message = WorkerErrorMessage::kNoData; - return TextErrorWithType(message, ErrorType::kEOF); + return TextErrorWithType(message, ErrorType::kEndOfFile); case HttpCode::NotFound: message = WorkerErrorMessage::kSourceNotFound; break; diff --git a/worker/api/cpp/src/http_client.h b/worker/api/cpp/src/http_client.h index 725af46011eae69bf660d647cb1a601e511e9a7f..31b838a62924f70fe83aecd5e70b4654421671aa 100644 --- a/worker/api/cpp/src/http_client.h +++ b/worker/api/cpp/src/http_client.h @@ -9,7 +9,7 @@ enum class HttpCode; class HttpClient { public: - virtual std::string Get(const std::string& uri, HttpCode* responce_code, Error* err) const noexcept = 0; + virtual std::string Get(const std::string& uri, HttpCode* response_code, Error* err) const noexcept = 0; virtual ~HttpClient() = default; }; diff --git a/worker/api/cpp/src/server_data_broker.cpp b/worker/api/cpp/src/server_data_broker.cpp index 4712df342d35e765852035bbe7113eb8041fdce8..0298c82db4d538a0c0202ff7efb82fa10b16752a 100644 --- a/worker/api/cpp/src/server_data_broker.cpp +++ b/worker/api/cpp/src/server_data_broker.cpp @@ -1,12 +1,12 @@ #include "server_data_broker.h" -#include "system_wrappers/system_io.h" +#include "io/io_factory.h" #include "curl_http_client.h" namespace hidra2 { ServerDataBroker::ServerDataBroker(const std::string& server_uri, const std::string& source_name): - io__{new hidra2::SystemIO}, httpclient__{new hidra2::CurlHttpClient}, + io__{GenerateDefaultIO()}, httpclient__{new CurlHttpClient}, server_uri_{server_uri}, source_name_{source_name} { } @@ -18,7 +18,7 @@ Error ServerDataBroker::GetFileInfoFromServer(FileInfo* info, const std::string& std::string full_uri = server_uri_ + "/database/" + source_name_ + "/" + operation; Error err; HttpCode code; - auto responce = httpclient__->Get(full_uri, &code, &err); + auto response = httpclient__->Get(full_uri, &code, &err); if (err != nullptr) { return err; @@ -26,11 +26,11 @@ Error ServerDataBroker::GetFileInfoFromServer(FileInfo* info, const std::string& err = HttpCodeToWorkerError(code); if (err != nullptr) { - err->Append(responce); + err->Append(response); return err; } - if (!info->SetFromJson(responce)) { + if (!info->SetFromJson(response)) { return TextError(WorkerErrorMessage::kErrorReadingSource); } return nullptr; @@ -55,4 +55,4 @@ Error ServerDataBroker::GetNext(FileInfo* info, FileData* data) { return error; } -} \ No newline at end of file +} diff --git a/worker/api/cpp/src/server_data_broker.h b/worker/api/cpp/src/server_data_broker.h index d77fff8bfddc4ea7e204e5b1a5c823a774967230..2081261b832c9884e8c762983f58c43adf91579e 100644 --- a/worker/api/cpp/src/server_data_broker.h +++ b/worker/api/cpp/src/server_data_broker.h @@ -2,7 +2,7 @@ #define HIDRA2_SERVER_DATA_BROKER_H #include "worker/data_broker.h" -#include "system_wrappers/io.h" +#include "io/io.h" #include "http_client.h" diff --git a/worker/api/cpp/unittests/test_folder_broker.cpp b/worker/api/cpp/unittests/test_folder_broker.cpp index 2190b9d27a89750d32eca1d10d1cb221b602a162..796183e246a718452536652f071b8507e9a7ee06 100644 --- a/worker/api/cpp/unittests/test_folder_broker.cpp +++ b/worker/api/cpp/unittests/test_folder_broker.cpp @@ -1,9 +1,10 @@ #include <gmock/gmock.h> +#include <unittests/MockIO.h> #include "gtest/gtest.h" #include "worker/data_broker.h" -#include "system_wrappers/io.h" -#include "system_wrappers/system_io.h" +#include "io/io.h" +#include "../../../../common/cpp/src/system_io/system_io.h" #include "../src/folder_data_broker.h" using hidra2::DataBrokerFactory; @@ -33,39 +34,13 @@ TEST(FolderDataBroker, SetCorrectIO) { delete data_broker; } - - -class FakeIO: public IO { +class FakeIO: public hidra2::MockIO { public: - virtual std::string ReadFileToString(const std::string& fname, Error* err)const noexcept override { + virtual std::string ReadFileToString(const std::string& fname, Error* err) const noexcept override { return "OK"; } - - virtual uint8_t* GetDataFromFileProxy(const std::string& fname, uint64_t fsize, SimpleError** err) const { - *err = nullptr; - return nullptr; - }; - - FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept override { - SimpleError* error; - auto data = GetDataFromFileProxy(fname, fsize, &error); - err->reset(error); - return FileData(data); - }; - - int open(const char* __file, int __oflag) const noexcept override { - return 0; - }; - - int close(int __fd)const noexcept override { - return 0; - }; - - uint64_t Read(int fd, uint8_t* array, uint64_t fsize, Error* err) const noexcept override { - return 0; - }; FileInfos FilesInFolder(const std::string& folder, Error* err) const override { *err = nullptr; FileInfos file_infos; @@ -84,7 +59,7 @@ class FakeIO: public IO { class IOFolderNotFound: public FakeIO { public: FileInfos FilesInFolder(const std::string& folder, Error* err) const override { - *err = hidra2::TextError(hidra2::IOErrors::kFileNotFound); + *err = hidra2::IOErrorTemplates::kFileNotFound.Generate(); return {}; } }; @@ -92,7 +67,7 @@ class IOFolderNotFound: public FakeIO { class IOFolderUnknownError: public FakeIO { public: FileInfos FilesInFolder(const std::string& folder, Error* err) const override { - *err = hidra2::TextError(hidra2::IOErrors::kUnknownError); + *err = hidra2::IOErrorTemplates::kUnknownIOError.Generate(); return {}; } }; @@ -108,7 +83,7 @@ class IOEmptyFolder: public FakeIO { class IOCannotOpenFile: public FakeIO { public: FileData GetDataFromFile(const std::string& fname, uint64_t fsize, Error* err) const noexcept override { - *err = hidra2::TextError(hidra2::IOErrors::kPermissionDenied); + *err = hidra2::IOErrorTemplates::kPermissionDenied.Generate(); return {}; }; }; @@ -146,7 +121,7 @@ TEST_F(FolderDataBrokerTests, CannotConnectWhenNoFolder) { auto return_code = data_broker->Connect(); - ASSERT_THAT(return_code->Explain(), Eq(hidra2::IOErrors::kFileNotFound)); + ASSERT_THAT(return_code->Explain(), Eq(hidra2::IOErrorTemplates::kFileNotFound.Generate()->Explain())); } TEST_F(FolderDataBrokerTests, ConnectReturnsUnknownIOError) { @@ -154,7 +129,7 @@ TEST_F(FolderDataBrokerTests, ConnectReturnsUnknownIOError) { auto return_code = data_broker->Connect(); - ASSERT_THAT(return_code->Explain(), Eq(hidra2::IOErrors::kUnknownError)); + ASSERT_THAT(return_code->Explain(), Eq(hidra2::IOErrorTemplates::kUnknownIOError.Generate()->Explain())); } TEST_F(FolderDataBrokerTests, GetNextWithoutConnectReturnsError) { @@ -200,12 +175,11 @@ TEST_F(FolderDataBrokerTests, GetNextFromEmptyFolderReturnsError) { FileInfo fi; auto err = data_broker->GetNext(&fi, nullptr); - ASSERT_THAT(err->GetErrorType(), Eq(hidra2::ErrorType::kEOF)); + ASSERT_TRUE(hidra2::ErrorTemplates::kEndOfFile == err); ASSERT_THAT(err->Explain(), Eq(hidra2::WorkerErrorMessage::kNoData)); } - TEST_F(FolderDataBrokerTests, GetNextReturnsErrorWhenFilePermissionsDenied) { data_broker->io__ = std::unique_ptr<IO> {new IOCannotOpenFile()}; data_broker->Connect(); @@ -213,16 +187,13 @@ TEST_F(FolderDataBrokerTests, GetNextReturnsErrorWhenFilePermissionsDenied) { FileData data; auto err = data_broker->GetNext(&fi, &data); - ASSERT_THAT(err->Explain(), Eq(hidra2::IOErrors::kPermissionDenied)); + ASSERT_THAT(err->Explain(), Eq(hidra2::IOErrorTemplates::kPermissionDenied.Generate()->Explain())); } class OpenFileMock : public FakeIO { - public: - MOCK_CONST_METHOD3(GetDataFromFileProxy, uint8_t* (const std::string&, uint64_t, SimpleError**)); }; - class GetDataFromFileTests : public Test { public: std::unique_ptr<FolderDataBroker> data_broker; @@ -240,16 +211,14 @@ class GetDataFromFileTests : public Test { }; TEST_F(GetDataFromFileTests, GetNextCallsGetDataFileWithFileName) { - EXPECT_CALL(mock, GetDataFromFileProxy("/path/to/file/1", _, _)). + EXPECT_CALL(mock, GetDataFromFile_t("/path/to/file/1", _, _)). WillOnce(DoAll(testing::SetArgPointee<2>(static_cast<SimpleError*>(nullptr)), testing::Return(nullptr))); data_broker->GetNext(&fi, &data); } - - TEST_F(GetDataFromFileTests, GetNextReturnsDataAndInfo) { - EXPECT_CALL(mock, GetDataFromFileProxy(_, _, _)). + EXPECT_CALL(mock, GetDataFromFile_t(_, _, _)). WillOnce(DoAll(testing::SetArgPointee<2>(nullptr), testing::Return(new uint8_t[1] {'1'}))); data_broker->GetNext(&fi, &data); @@ -259,26 +228,24 @@ TEST_F(GetDataFromFileTests, GetNextReturnsDataAndInfo) { } - - TEST_F(GetDataFromFileTests, GetNextReturnsErrorWhenCannotReadData) { - EXPECT_CALL(mock, GetDataFromFileProxy(_, _, _)). - WillOnce(DoAll(testing::SetArgPointee<2>(new SimpleError(hidra2::IOErrors::kReadError)), testing::Return(nullptr))); + EXPECT_CALL(mock, GetDataFromFile_t(_, _, _)). + WillOnce(DoAll(testing::SetArgPointee<2>(hidra2::IOErrorTemplates::kReadError.Generate().release()), + testing::Return(nullptr))); auto err = data_broker->GetNext(&fi, &data); - ASSERT_THAT(err->Explain(), Eq(hidra2::IOErrors::kReadError)); + ASSERT_THAT(err->Explain(), Eq(hidra2::IOErrorTemplates::kReadError.Generate()->Explain())); } - TEST_F(GetDataFromFileTests, GetNextReturnsErrorWhenCannotAllocateData) { - EXPECT_CALL(mock, GetDataFromFileProxy(_, _, _)). - WillOnce(DoAll(testing::SetArgPointee<2>(new SimpleError(hidra2::IOErrors::kMemoryAllocationError)), + EXPECT_CALL(mock, GetDataFromFile_t(_, _, _)). + WillOnce(DoAll(testing::SetArgPointee<2>(hidra2::ErrorTemplates::kMemoryAllocationError.Generate().release()), testing::Return(nullptr))); auto err = data_broker->GetNext(&fi, &data); - ASSERT_THAT(err->Explain(), Eq(hidra2::IOErrors::kMemoryAllocationError)); + ASSERT_THAT(err->Explain(), Eq(hidra2::ErrorTemplates::kMemoryAllocationError.Generate()->Explain())); } diff --git a/worker/api/cpp/unittests/test_server_broker.cpp b/worker/api/cpp/unittests/test_server_broker.cpp index 9362bc61f6419b22f79e00a6cf3b6a969e661989..48296c46fdb53baa0cdca421aa692b6e22c34de8 100644 --- a/worker/api/cpp/unittests/test_server_broker.cpp +++ b/worker/api/cpp/unittests/test_server_broker.cpp @@ -2,8 +2,8 @@ #include "gtest/gtest.h" #include "worker/data_broker.h" -#include "system_wrappers/io.h" -#include "system_wrappers/system_io.h" +#include "io/io.h" +#include "../../../../common/cpp/src/system_io/system_io.h" #include "../src/server_data_broker.h" #include "../src/curl_http_client.h" #include "unittests/MockIO.h" @@ -32,7 +32,7 @@ using ::testing::Mock; using ::testing::NiceMock; using ::testing::Return; using ::testing::SetArgPointee; - +using ::testing::SetArgReferee; namespace { @@ -63,11 +63,11 @@ class ServerDataBrokerTests : public Test { data_broker->io__.release(); data_broker->httpclient__.release(); } - void MockGet(const std::string& responce) { + void MockGet(const std::string& response) { EXPECT_CALL(mock_http_client, Get_t(_, _, _)).WillOnce(DoAll( SetArgPointee<1>(HttpCode::OK), SetArgPointee<2>(nullptr), - Return(responce) + Return(response) )); } @@ -114,7 +114,7 @@ TEST_F(ServerDataBrokerTests, GetNextReturnsEOFFromHttpClient) { auto err = data_broker->GetNext(&info, nullptr); ASSERT_THAT(err->Explain(), HasSubstr(hidra2::WorkerErrorMessage::kNoData)); - ASSERT_THAT(err->GetErrorType(), hidra2::ErrorType::kEOF); + ASSERT_THAT(err->GetErrorType(), hidra2::ErrorType::kEndOfFile); } @@ -145,7 +145,7 @@ TEST_F(ServerDataBrokerTests, GetNextReturnsFileInfo) { TEST_F(ServerDataBrokerTests, GetNextReturnsParseError) { - MockGet("error_responce"); + MockGet("error_response"); auto err = data_broker->GetNext(&info, nullptr); ASSERT_THAT(err->Explain(), Eq(hidra2::WorkerErrorMessage::kErrorReadingSource)); @@ -153,13 +153,12 @@ TEST_F(ServerDataBrokerTests, GetNextReturnsParseError) { TEST_F(ServerDataBrokerTests, GetNextReturnsIfNoDtataNeeded) { - MockGet("error_responce"); + MockGet("error_response"); EXPECT_CALL( mock_io, GetDataFromFile_t(_, _, _)).Times(0); data_broker->GetNext(&info, nullptr); } - TEST_F(ServerDataBrokerTests, GetNextCallsReadFromFile) { auto to_send = CreateFI(); auto json = to_send.Json(); @@ -167,7 +166,7 @@ TEST_F(ServerDataBrokerTests, GetNextCallsReadFromFile) { MockGet(json); EXPECT_CALL(mock_io, GetDataFromFile_t("name", 100, _)). - WillOnce(DoAll(SetArgPointee<2>(new SimpleError{hidra2::IOErrors::kReadError}), testing::Return(nullptr))); + WillOnce(DoAll(SetArgPointee<2>(new hidra2::SimpleError{"s"}), testing::Return(nullptr))); FileData data; data_broker->GetNext(&info, &data); diff --git a/worker/tools/folder_to_db/src/folder_db_importer.cpp b/worker/tools/folder_to_db/src/folder_db_importer.cpp index 916f06c10bfbc6cf0061a1361de414bb48523b80..1bc002c7e1dda0e3502b97527fb314e7795cc531 100644 --- a/worker/tools/folder_to_db/src/folder_db_importer.cpp +++ b/worker/tools/folder_to_db/src/folder_db_importer.cpp @@ -3,7 +3,7 @@ #include <future> #include <algorithm> -#include "system_wrappers/system_io.h" +#include "io/io_factory.h" #include "database/database.h" @@ -12,7 +12,7 @@ namespace hidra2 { using std::chrono::high_resolution_clock; FolderToDbImporter::FolderToDbImporter() : - io__{new hidra2::SystemIO}, db_factory__{new hidra2::DatabaseFactory} { + io__{GenerateDefaultIO()}, db_factory__{new hidra2::DatabaseFactory} { } Error FolderToDbImporter::ConnectToDb(const std::unique_ptr<hidra2::Database>& db) const { diff --git a/worker/tools/folder_to_db/src/folder_db_importer.h b/worker/tools/folder_to_db/src/folder_db_importer.h index aecadd203618af74b16bb46ed6aa88cc110750b7..619d8d2646e6aca866b4553c67198c3f20887e99 100644 --- a/worker/tools/folder_to_db/src/folder_db_importer.h +++ b/worker/tools/folder_to_db/src/folder_db_importer.h @@ -6,7 +6,7 @@ #include <future> -#include "system_wrappers/io.h" +#include "io/io.h" #include "database/database.h" #include "common/error.h" namespace hidra2 { diff --git a/worker/tools/folder_to_db/unittests/test_folder_to_db.cpp b/worker/tools/folder_to_db/unittests/test_folder_to_db.cpp index 196ff2a7c223876182931f85a48b9ee89e292390..cd9f6991b7b0284efe3761217db56baebeeb43fe 100644 --- a/worker/tools/folder_to_db/unittests/test_folder_to_db.cpp +++ b/worker/tools/folder_to_db/unittests/test_folder_to_db.cpp @@ -2,8 +2,8 @@ #include "gtest/gtest.h" #include <thread> -#include "system_wrappers/io.h" -#include "system_wrappers/system_io.h" +#include "io/io.h" +#include "../../../../common/cpp/src/system_io/system_io.h" #include "database/database.h"