From 92d2585e874d12adaea9ac6ba96a2b72ed5add25 Mon Sep 17 00:00:00 2001
From: Sergey Yakubov <sergey.yakubov@desy.de>
Date: Tue, 27 Mar 2018 00:15:02 +0200
Subject: [PATCH] write file to disk

---
 common/cpp/include/system_wrappers/io.h       |  2 +
 .../cpp/include/system_wrappers/system_io.h   |  1 +
 common/cpp/include/unittests/MockIO.h         |  8 +++
 common/cpp/src/system_io/system_io.cpp        | 17 ++++++
 receiver/src/receiver_error.h                 |  5 ++
 receiver/src/request.cpp                      | 15 +++++-
 receiver/src/request.h                        |  4 ++
 receiver/src/request_handler_file_write.cpp   | 14 ++++-
 receiver/src/request_handler_file_write.h     |  2 +
 .../test_request_handler_file_write.cpp       | 54 +++++++++++++++++++
 .../transfer_single_file/check_linux.sh       |  4 +-
 .../transfer_single_file/check_windows.bat    |  2 +
 12 files changed, 124 insertions(+), 4 deletions(-)

diff --git a/common/cpp/include/system_wrappers/io.h b/common/cpp/include/system_wrappers/io.h
index ebb62dfa5..c10e3591d 100644
--- a/common/cpp/include/system_wrappers/io.h
+++ b/common/cpp/include/system_wrappers/io.h
@@ -218,6 +218,8 @@ class IO {
     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,
diff --git a/common/cpp/include/system_wrappers/system_io.h b/common/cpp/include/system_wrappers/system_io.h
index 52b7332bb..2a1703d7f 100644
--- a/common/cpp/include/system_wrappers/system_io.h
+++ b/common/cpp/include/system_wrappers/system_io.h
@@ -101,6 +101,7 @@ class SystemIO final : public IO {
     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;
diff --git a/common/cpp/include/unittests/MockIO.h b/common/cpp/include/unittests/MockIO.h
index 1ce54ffed..262245a8b 100644
--- a/common/cpp/include/unittests/MockIO.h
+++ b/common/cpp/include/unittests/MockIO.h
@@ -170,8 +170,16 @@ class MockIO : public IO {
         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;
diff --git a/common/cpp/src/system_io/system_io.cpp b/common/cpp/src/system_io/system_io.cpp
index 5b023f3ec..d91eda9a4 100644
--- a/common/cpp/src/system_io/system_io.cpp
+++ b/common/cpp/src/system_io/system_io.cpp
@@ -121,6 +121,23 @@ void hidra2::SystemIO::CreateNewDirectory(const std::string& directory_name, Err
     }
 }
 
+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) {
diff --git a/receiver/src/receiver_error.h b/receiver/src/receiver_error.h
index b82705288..63da34f6f 100644
--- a/receiver/src/receiver_error.h
+++ b/receiver/src/receiver_error.h
@@ -7,6 +7,7 @@ namespace hidra2 {
 
 enum class ReceiverErrorType {
     kInvalidOpCode,
+    kBadRequest
 };
 
 //TODO Make a marco to create error class and error template class
@@ -51,6 +52,10 @@ namespace ReceiverErrorTemplates {
 auto const kInvalidOpCode = ReceiverErrorTemplate {
                                 "Invalid Opcode", ReceiverErrorType::kInvalidOpCode
                             };
+auto const kBadRequest = ReceiverErrorTemplate {
+                             "Bad request", ReceiverErrorType::kBadRequest
+                         };
+
 };
 }
 
diff --git a/receiver/src/request.cpp b/receiver/src/request.cpp
index 48d85e9de..ef2794098 100644
--- a/receiver/src/request.cpp
+++ b/receiver/src/request.cpp
@@ -4,7 +4,7 @@
 namespace hidra2 {
 
 Request::Request(const GenericNetworkRequestHeader& header,
-                 SocketDescriptor socket_fd) : io__{new SystemIO}, request_header_{header}, socket_fd_{socket_fd} {
+                 SocketDescriptor socket_fd) : io__{new SystemIO}, request_header_(header), socket_fd_{socket_fd} {
 }
 
 Error Request::AllocateDataBuffer() {
@@ -55,6 +55,19 @@ void Request::AddHandler(const RequestHandler* 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 {
diff --git a/receiver/src/request.h b/receiver/src/request.h
index 8efcaf784..dd45a8c74 100644
--- a/receiver/src/request.h
+++ b/receiver/src/request.h
@@ -18,6 +18,10 @@ class Request {
     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();
diff --git a/receiver/src/request_handler_file_write.cpp b/receiver/src/request_handler_file_write.cpp
index 6b509495d..2796f6321 100644
--- a/receiver/src/request_handler_file_write.cpp
+++ b/receiver/src/request_handler_file_write.cpp
@@ -1,10 +1,20 @@
 #include "request_handler_file_write.h"
 #include "system_wrappers/system_io.h"
-
+#include "request.h"
 namespace hidra2 {
 
 Error RequestHandlerFileWrite::ProcessRequest(const Request& request) const {
-    return nullptr;
+    auto fsize = request.GetDataSize();
+    if (fsize <= 0 || fsize > kMaxFileSize) {
+        return ReceiverErrorTemplates::kBadRequest.Generate();
+    }
+
+    const FileData& data = request.GetData();
+
+    auto fname = request.GetFileName();
+
+    return io__->WriteDataToFile("files/" + fname, data, fsize);
+
 }
 
 RequestHandlerFileWrite::RequestHandlerFileWrite() : io__{new SystemIO} {
diff --git a/receiver/src/request_handler_file_write.h b/receiver/src/request_handler_file_write.h
index bbffa8d3f..fb56d4e25 100644
--- a/receiver/src/request_handler_file_write.h
+++ b/receiver/src/request_handler_file_write.h
@@ -7,6 +7,8 @@
 
 namespace hidra2 {
 
+const uint64_t kMaxFileSize = uint64_t(1024) * 1024 * 1024 * 2; //2GB
+
 class RequestHandlerFileWrite final: public RequestHandler {
   public:
     RequestHandlerFileWrite();
diff --git a/receiver/unittests/test_request_handler_file_write.cpp b/receiver/unittests/test_request_handler_file_write.cpp
index 2996f7159..d330b855c 100644
--- a/receiver/unittests/test_request_handler_file_write.cpp
+++ b/receiver/unittests/test_request_handler_file_write.cpp
@@ -9,6 +9,7 @@
 
 using ::testing::Test;
 using ::testing::Return;
+using ::testing::ReturnRef;
 using ::testing::_;
 using ::testing::DoAll;
 using ::testing::SetArgReferee;
@@ -44,7 +45,11 @@ 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),
@@ -57,4 +62,53 @@ class FileWriteHandlerTests : public Test {
 
 };
 
+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())
+        Return(nullptr)
+    );
+
+    auto err = handler.ProcessRequest(*mock_request);
+
+    ASSERT_THAT(err, Eq(nullptr));
+}
+
+
 }
\ No newline at end of file
diff --git a/tests/producer_receiver/transfer_single_file/check_linux.sh b/tests/producer_receiver/transfer_single_file/check_linux.sh
index ae45360ab..163d4ab08 100644
--- a/tests/producer_receiver/transfer_single_file/check_linux.sh
+++ b/tests/producer_receiver/transfer_single_file/check_linux.sh
@@ -14,6 +14,8 @@ nohup $2 &>/dev/null &
 sleep 0.3
 receiverid=`echo $!`
 
+mkdir files
+
 $1 localhost:4200 100 1
 
-du -b files/0.bin | cut -f1 | grep 100
+ls -ln files/0.bin | awk '{ print $5 }'| grep 100
diff --git a/tests/producer_receiver/transfer_single_file/check_windows.bat b/tests/producer_receiver/transfer_single_file/check_windows.bat
index a6076b28b..9413a82a2 100644
--- a/tests/producer_receiver/transfer_single_file/check_windows.bat
+++ b/tests/producer_receiver/transfer_single_file/check_windows.bat
@@ -5,6 +5,8 @@ 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
-- 
GitLab