Add Cynara::StorageDeserializer
authorAleksander Zdyb <a.zdyb@partner.samsung.com>
Thu, 26 Jun 2014 12:36:10 +0000 (14:36 +0200)
committerRafal Krypa <r.krypa@samsung.com>
Thu, 3 Jul 2014 12:19:10 +0000 (14:19 +0200)
Change-Id: I402276fc32aec5839dc5e20b323fbc639dc6ff14

src/service/storage/StorageDeserializer.cpp [new file with mode: 0644]
src/service/storage/StorageDeserializer.h [new file with mode: 0644]
src/service/storage/StorageSerializer.cpp
src/service/storage/StorageSerializer.h
test/CMakeLists.txt
test/storage/serializer/load.cpp [new file with mode: 0644]

diff --git a/src/service/storage/StorageDeserializer.cpp b/src/service/storage/StorageDeserializer.cpp
new file mode 100644 (file)
index 0000000..a2a14bf
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        StorageDeserializer.cpp
+ * @author      Aleksander Zdyb <a.zdyb@partner.samsung.com>
+ * @version     1.0
+ * @brief       Methods implementation of Cynara::StorageDeserializer
+ */
+
+#include <exceptions/BucketRecordCorruptedException.h>
+#include <storage/StorageDeserializer.h>
+#include <storage/StorageSerializer.h>
+#include <types/Policy.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+
+namespace Cynara {
+
+PolicyCollection StorageDeserializer::loadPolicies(std::istream &is) {
+    PolicyCollection policies;
+
+    // TODO: Get someone smart to do error checking on stream
+    for(std::size_t lineNum = 1; !is.eof(); ++lineNum) {
+        std::string line;
+        std::getline(is, line, StorageSerializer::recordSeparator());
+
+        if (line.empty())
+            break;
+
+        try {
+            std::size_t beginToken = 0;
+            auto policyKey = parseKey(line, beginToken);
+            auto policyType = parsePolicyType(line, beginToken);
+            auto metadata = parseMetadata(line, beginToken);
+            PolicyResult policyResult(policyType, metadata);
+            policies.push_back(std::make_shared<Policy>(policyKey, policyResult));
+        } catch (const BucketRecordCorruptedException &ex) {
+            throw ex.withLineNumber(lineNum);
+        }
+    }
+
+    return policies;
+}
+
+PolicyKey StorageDeserializer::parseKey(const std::string &line, std::size_t &beginToken) {
+    std::array<std::string, 3> keyFeatures;
+
+    for(auto tokenNum = 0; tokenNum < keyFeatures.size(); ++tokenNum) {
+        auto endToken = line.find(StorageSerializer::fieldSeparator(), beginToken);
+        if (endToken != std::string::npos) {
+            keyFeatures[tokenNum] = line.substr(beginToken, endToken - beginToken);
+            beginToken = endToken + 1;
+        } else {
+            throw BucketRecordCorruptedException(line);
+        }
+    }
+
+    return PolicyKey(keyFeatures[0], keyFeatures[1], keyFeatures[2]);
+}
+
+PolicyType StorageDeserializer::parsePolicyType(const std::string &line, std::size_t &beginToken) {
+    PolicyType policyType;
+    try {
+        size_t newBegin = 0;
+        policyType = std::stoi(line.substr(beginToken), &newBegin, 16);
+        beginToken += newBegin;
+    } catch(...) {
+        throw BucketRecordCorruptedException(line);
+    }
+
+    return policyType;
+}
+
+PolicyResult::PolicyMetadata StorageDeserializer::parseMetadata(const std::string &line,
+                                                                std::size_t &beginToken) {
+    if (beginToken < line.size()) {
+        auto ret = line.substr(beginToken + 1);
+        beginToken = line.size();
+        return ret;
+    }
+
+    return std::string();
+}
+
+} /* namespace Cynara */
diff --git a/src/service/storage/StorageDeserializer.h b/src/service/storage/StorageDeserializer.h
new file mode 100644 (file)
index 0000000..66804c5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        StorageDeserializer.h
+ * @author      Aleksander Zdyb <a.zdyb@partner.samsung.com>
+ * @version     1.0
+ * @brief       Headers for Cynara::StorageDeserializer
+ */
+#ifndef SRC_SERVICE_STORAGE_STORAGEDESERIALIZER_H_
+#define SRC_SERVICE_STORAGE_STORAGEDESERIALIZER_H_
+
+#include <types/PolicyCollection.h>
+#include <types/PolicyKey.h>
+#include <types/PolicyResult.h>
+#include <types/PolicyType.h>
+
+#include <istream>
+
+namespace Cynara {
+
+class StorageDeserializer {
+public:
+    static PolicyCollection loadPolicies(std::istream &is);
+
+protected:
+    static PolicyKey parseKey(const std::string &line, std::size_t &beginToken);
+    static PolicyType parsePolicyType(const std::string &line, std::size_t &beginToken);
+    static PolicyResult::PolicyMetadata parseMetadata(const std::string &line,
+                                                      std::size_t &beginToken);
+};
+
+} /* namespace Cynara */
+
+#endif /* SRC_SERVICE_STORAGE_STORAGEDESERIALIZER_H_ */
index 5ab6e08..8813fc9 100644 (file)
 
 #include "StorageSerializer.h"
 
-#include "types/PolicyCollection.h"
 #include "types/PolicyBucket.h"
+#include "types/PolicyCollection.h"
 
 #include <algorithm>
 
 namespace Cynara {
 
-std::string StorageSerializer::fieldSeparator = ";";
-std::string StorageSerializer::recordSeparator = "\n";
+char StorageSerializer::m_fieldSeparator = ';';
+char StorageSerializer::m_recordSeparator = '\n';
 
 StorageSerializer::StorageSerializer(std::ostream &os) : m_outStream(os) {}
 
index 06abd73..33108a4 100644 (file)
@@ -45,13 +45,13 @@ protected:
     inline void dumpFields(const Arg1 arg1, const Args&... args) {
         dump(arg1);
         if (sizeof...(Args) > 0) {
-            outStream() << fieldSeparator;
+            outStream() << fieldSeparator();
         }
         dumpFields(args...);
     }
 
     inline void dumpFields(void) {
-        outStream() << recordSeparator;
+        outStream() << recordSeparator();
     }
 
     void dump(const PolicyKey &key);
@@ -67,8 +67,17 @@ protected:
 private:
     std::ostream &m_outStream;
 
-    static std::string fieldSeparator;
-    static std::string recordSeparator;
+    static char m_fieldSeparator;
+    static char m_recordSeparator;
+
+public:
+    static const char &fieldSeparator(void) {
+        return m_fieldSeparator;
+    }
+
+    static const char &recordSeparator(void) {
+        return m_recordSeparator;
+    }
 };
 
 } /* namespace Cynara */
index 7068a51..daac15c 100644 (file)
@@ -26,6 +26,7 @@ SET(CYNARA_SOURCES_FOR_TESTS
     ${CYNARA_SRC}/service/storage/Storage.cpp
     ${CYNARA_SRC}/common/types/PolicyBucket.cpp
     ${CYNARA_SRC}/service/storage/InMemoryStorageBackend.cpp
+    ${CYNARA_SRC}/service/storage/StorageDeserializer.cpp
     ${CYNARA_SRC}/service/storage/StorageSerializer.cpp
     ${CYNARA_SRC}/common/types/PolicyKey.cpp
     ${CYNARA_SRC}/common/types/PolicyType.cpp
@@ -41,6 +42,7 @@ SET(CYNARA_TESTS_SOURCES
     storage/inmemorystoragebackend/search.cpp
     storage/inmemorystoragebackend/buckets.cpp
     storage/serializer/dump.cpp
+    storage/serializer/load.cpp
     common/types/policybucket.cpp
     helpers.cpp
 )
@@ -51,6 +53,7 @@ INCLUDE_DIRECTORIES(
     ${CYNARA_SRC}
     ${CYNARA_SRC}/common
     ${CYNARA_SRC}/include
+    ${CYNARA_SRC}/service
 )
 
 ADD_EXECUTABLE(cynara-tests
diff --git a/test/storage/serializer/load.cpp b/test/storage/serializer/load.cpp
new file mode 100644 (file)
index 0000000..d67af08
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/*
+ * @file        load.cpp
+ * @author      Aleksander Zdyb <a.zdyb@partner.samsung.com>
+ * @version     1.0
+ * @brief       Tests for Cynara::StorageDeserializer
+ */
+
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <exceptions/BucketRecordCorruptedException.h>
+#include <service/storage/StorageDeserializer.h>
+#include <types/Policy.h>
+
+#include "../../helpers.h"
+
+#include <memory>
+#include <sstream>
+#include <tuple>
+#include <vector>
+
+using namespace Cynara;
+
+MATCHER_P(PolicyPtrEq, policy, "") {
+    return std::tie(policy->key(), policy->result())
+        == std::tie(arg->key(), arg->result());
+}
+
+class DeserializerFixture : public ::testing::Test {
+public:
+    virtual ~DeserializerFixture() = default;
+
+    PolicyPtr createPolicy(const PolicyKey &pk, const PolicyResult &pr) {
+        return std::make_shared<Policy>(pk, pr);
+    }
+
+    void checkCorruptedData(const std::string &data, const std::string &corruptedLine,
+            size_t corruptedLineNumber) {
+        std::istringstream ss(data);
+        EXPECT_THROW(StorageDeserializer::loadPolicies(ss), BucketRecordCorruptedException);
+
+        ss.seekg(0);
+        try {
+            StorageDeserializer::loadPolicies(ss);
+        } catch (const BucketRecordCorruptedException &ex) {
+            ASSERT_EQ(corruptedLine, ex.line());
+            ASSERT_EQ(corruptedLineNumber, ex.lineNumber());
+        }
+    }
+};
+
+TEST_F(DeserializerFixture, load_empty) {
+    using ::testing::IsEmpty;
+
+    std::istringstream ss;
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    ASSERT_THAT(policies, IsEmpty());
+}
+
+TEST_F(DeserializerFixture, load_1) {
+    using ::testing::UnorderedElementsAre;
+
+    std::istringstream ss("c;u;p;0;meta");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    auto expectedPolicy = createPolicy({ "c", "u", "p" }, { PredefinedPolicyType::DENY, "meta" });
+
+    ASSERT_THAT(policies, UnorderedElementsAre(PolicyPtrEq(expectedPolicy)));
+}
+
+TEST_F(DeserializerFixture, load_1_allow) {
+    using ::testing::UnorderedElementsAre;
+
+    std::istringstream ss("c;u;p;0xFFFF;meta");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    auto expectedPolicy = createPolicy({ "c", "u", "p" }, { PredefinedPolicyType::ALLOW, "meta" });
+
+    ASSERT_THAT(policies, UnorderedElementsAre(PolicyPtrEq(expectedPolicy)));
+}
+
+TEST_F(DeserializerFixture, load_1_no_meta_sep) {
+    using ::testing::UnorderedElementsAre;
+
+    std::istringstream ss("c;u;p;0;");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    auto expectedPolicy = createPolicy({ "c", "u", "p" }, { PredefinedPolicyType::DENY, "" });
+
+    ASSERT_THAT(policies, UnorderedElementsAre(PolicyPtrEq(expectedPolicy)));
+}
+
+TEST_F(DeserializerFixture, load_1_no_meta_no_sep) {
+    using ::testing::UnorderedElementsAre;
+
+    std::istringstream ss("c;u;p;0");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    auto expectedPolicy = createPolicy({ "c", "u", "p" }, { PredefinedPolicyType::DENY, "" });
+
+    ASSERT_THAT(policies, UnorderedElementsAre(PolicyPtrEq(expectedPolicy)));
+}
+
+TEST_F(DeserializerFixture, load_2) {
+    using ::testing::UnorderedElementsAre;
+
+    std::istringstream ss("c;u;p;0;meta\n"
+                          "c;u;p;0;meta\n");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    auto expectedPolicy = createPolicy({ "c", "u", "p" }, { PredefinedPolicyType::DENY, "meta" });
+
+    ASSERT_THAT(policies, UnorderedElementsAre(PolicyPtrEq(expectedPolicy),
+            PolicyPtrEq(expectedPolicy)));
+}
+
+TEST_F(DeserializerFixture, load_mixed) {
+    using ::testing::UnorderedElementsAre;
+    using ::testing::UnorderedElementsAreArray;
+
+    std::istringstream ss("c1;u1;p1;0;meta\n"
+                          "c2;u2;p2;0xFFFF;meta2\n"
+                          "c2;u2;p2;0xFFFF;\n"
+                          "c1;u1;p3;0xFFFE;bucket\n");
+
+    auto policies = StorageDeserializer::loadPolicies(ss);
+    PolicyCollection expectedPolices = {
+        createPolicy({ "c1", "u1", "p1" }, { PredefinedPolicyType::DENY, "meta" }),
+        createPolicy({ "c2", "u2", "p2" }, { PredefinedPolicyType::ALLOW, "meta2" }),
+        createPolicy({ "c2", "u2", "p2" }, { PredefinedPolicyType::ALLOW, "" }),
+        createPolicy({ "c1", "u1", "p3" }, { PredefinedPolicyType::BUCKET, "bucket" })
+    };
+
+    // How to do it more elegantly?
+    ASSERT_THAT(policies, UnorderedElementsAre(
+        PolicyPtrEq(expectedPolices.at(0)),
+        PolicyPtrEq(expectedPolices.at(1)),
+        PolicyPtrEq(expectedPolices.at(2)),
+        PolicyPtrEq(expectedPolices.at(3))
+    ));
+}
+
+TEST_F(DeserializerFixture, load_no_client) {
+    auto data = "u;p;0;meta";
+    checkCorruptedData(data, data, 1);
+}
+
+TEST_F(DeserializerFixture, load_no_type) {
+    auto data = "c;u;p;meta";
+    checkCorruptedData(data, data, 1);
+}
+
+TEST_F(DeserializerFixture, load_all_missing) {
+    auto data = ";;;";
+    checkCorruptedData(data, data, 1);
+}
+
+TEST_F(DeserializerFixture, load_invalid_type) {
+    auto data = "c;u;p;X";
+    checkCorruptedData(data, data, 1);
+}
+
+TEST_F(DeserializerFixture, load_invalid_multiline) {
+    auto data = "c1;u1;p1;0;meta\n"
+                "c;u;p;X\n"
+                "c1;u1;p1;0;meta\n";
+    checkCorruptedData(data, "c;u;p;X", 2);
+}