Add generic XML parser + tests. 98/39298/9
authorMaciej J. Karpiuk <m.karpiuk2@samsung.com>
Wed, 6 May 2015 13:20:41 +0000 (15:20 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Tue, 19 May 2015 14:04:45 +0000 (07:04 -0700)
Change-Id: I44494b0e3034cb0e6e258bc9b8da8cadb5e2be70

packaging/key-manager.spec
src/CMakeLists.txt
src/manager/initial-values/parser.cpp [new file with mode: 0644]
src/manager/initial-values/parser.h [new file with mode: 0644]
tests/CMakeLists.txt
tests/XML_1_okay.xml [new file with mode: 0644]
tests/XML_1_okay.xsd [new file with mode: 0644]
tests/XML_1_wrong.xml [new file with mode: 0644]
tests/XML_1_wrong.xsd [new file with mode: 0644]
tests/XML_2_structure.xml [new file with mode: 0644]
tests/test_xml-parser.cpp [new file with mode: 0644]

index 3916e13..7855988 100644 (file)
@@ -18,6 +18,7 @@ BuildRequires: pkgconfig(libsmack)
 BuildRequires: pkgconfig(libsystemd-daemon)
 BuildRequires: pkgconfig(vconf)
 BuildRequires: pkgconfig(libsystemd-journal)
+BuildRequires: pkgconfig(libxml-2.0)
 BuildRequires: boost-devel
 Requires: libkey-manager-common = %{version}-%{release}
 %{?systemd_requires}
@@ -73,6 +74,7 @@ Central Key Manager package (client-devel)
 %package -n key-manager-tests
 Summary:    Internal test for key-manager
 Group:      Development
+BuildRequires: pkgconfig(libxml-2.0)
 Requires:   boost-test
 Requires:   key-manager = %{version}-%{release}
 
@@ -133,6 +135,11 @@ mkdir -p %{buildroot}/usr/share/ckm-db-test
 cp tests/testme_ver1.db %{buildroot}/usr/share/ckm-db-test/
 cp tests/testme_ver2.db %{buildroot}/usr/share/ckm-db-test/
 cp tests/testme_ver3.db %{buildroot}/usr/share/ckm-db-test/
+cp tests/XML_1_okay.xml %{buildroot}/usr/share/ckm-db-test/
+cp tests/XML_1_okay.xsd %{buildroot}/usr/share/ckm-db-test/
+cp tests/XML_1_wrong.xml %{buildroot}/usr/share/ckm-db-test/
+cp tests/XML_1_wrong.xsd %{buildroot}/usr/share/ckm-db-test/
+cp tests/XML_2_structure.xml %{buildroot}/usr/share/ckm-db-test/
 mkdir -p %{buildroot}/etc/gumd/userdel.d/
 cp data/gumd/10_key-manager.post %{buildroot}/etc/gumd/userdel.d/
 
@@ -265,6 +272,11 @@ fi
 %{_datadir}/ckm-db-test/testme_ver1.db
 %{_datadir}/ckm-db-test/testme_ver2.db
 %{_datadir}/ckm-db-test/testme_ver3.db
+%{_datadir}/ckm-db-test/XML_1_okay.xml
+%{_datadir}/ckm-db-test/XML_1_okay.xsd
+%{_datadir}/ckm-db-test/XML_1_wrong.xml
+%{_datadir}/ckm-db-test/XML_1_wrong.xsd
+%{_datadir}/ckm-db-test/XML_2_structure.xml
 %{_bindir}/ckm_so_loader
 
 %files -n key-manager-pam-plugin
index 92635be..ce6680a 100644 (file)
@@ -6,6 +6,7 @@ PKG_CHECK_MODULES(KEY_MANAGER_DEP
     libsystemd-daemon
     capi-base-common
     vconf
+    libxml-2.0
     REQUIRED
     )
 FIND_PACKAGE(Threads REQUIRED)
@@ -33,6 +34,7 @@ SET(KEY_MANAGER_SOURCES
     ${KEY_MANAGER_PATH}/service/db-crypto.cpp
     ${KEY_MANAGER_PATH}/service/ocsp-service.cpp
     ${KEY_MANAGER_PATH}/service/ocsp-logic.cpp
+    ${KEY_MANAGER_PATH}/initial-values/parser.cpp
     ${KEY_MANAGER_PATH}/dpl/core/src/assert.cpp
     ${KEY_MANAGER_PATH}/dpl/db/src/sql_connection.cpp
     ${KEY_MANAGER_PATH}/dpl/db/src/naive_synchronization_object.cpp
@@ -59,6 +61,7 @@ INCLUDE_DIRECTORIES(
     ${KEY_MANAGER_PATH}/main
     ${KEY_MANAGER_PATH}/common
     ${KEY_MANAGER_PATH}/service
+    ${KEY_MANAGER_PATH}/initial-values
     ${KEY_MANAGER_PATH}/sqlcipher
     ${KEY_MANAGER_PATH}/dpl/core/include
     ${KEY_MANAGER_PATH}/dpl/log/include
diff --git a/src/manager/initial-values/parser.cpp b/src/manager/initial-values/parser.cpp
new file mode 100644 (file)
index 0000000..06f8143
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ *  Copyright (c) 2000 - 2015 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        parser.cpp
+ * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version     1.0
+ * @brief       XML parser class implementation.
+ */
+
+#include <string>
+#include <string.h>
+#include <algorithm>
+#include <exception>
+#include <libxml/parser.h>
+#include <libxml/valid.h>
+#include <libxml/xmlschemas.h>
+#include <parser.h>
+#include <dpl/log/log.h>
+
+using namespace XML;
+
+namespace
+{
+const char * const WHITESPACE = " \n\r\t";
+std::string trim_left(const std::string& s)
+{
+    size_t startpos = s.find_first_not_of(WHITESPACE);
+    return (startpos == std::string::npos) ? "" : s.substr(startpos);
+}
+
+std::string trim_right(const std::string& s)
+{
+    size_t endpos = s.find_last_not_of(WHITESPACE);
+    return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
+}
+std::string trim(const std::string& s)
+{
+    return trim_right(trim_left(s));
+}
+}
+
+Parser::Parser(const char *XML_filename)
+    : m_errorCb(0)
+{
+    if(XML_filename)
+        m_XMLfile = XML_filename;
+    memset(&m_saxHandler, 0, sizeof(m_saxHandler));
+    m_saxHandler.startElement = &Parser::StartElement;
+    m_saxHandler.endElement = &Parser::EndElement;
+    m_saxHandler.characters = &Parser::Characters;
+    m_saxHandler.error = &Parser::Error;
+    m_saxHandler.warning = &Parser::Warning;
+}
+Parser::~Parser()
+{
+    xmlCleanupParser();
+}
+
+int Parser::Validate(const char *XSD_schema)
+{
+    if(!XSD_schema) {
+        LogError("no XSD file path given");
+        return ERROR_INVALID_ARGUMENT;
+    }
+
+    int retCode;
+    std::unique_ptr<xmlSchemaParserCtxt, void(*)(xmlSchemaParserCtxtPtr)>
+            parserCtxt(xmlSchemaNewParserCtxt(XSD_schema),
+                       [](xmlSchemaParserCtxtPtr ctx){ xmlSchemaFreeParserCtxt(ctx); });
+    if(!parserCtxt) {
+        LogError("XSD file path is invalid");
+        return ERROR_INVALID_ARGUMENT;
+    }
+
+    std::unique_ptr<xmlSchema, void(*)(xmlSchemaPtr)>
+        schema(xmlSchemaParse(parserCtxt.get()),
+                       [](xmlSchemaPtr schemaPtr){ xmlSchemaFree(schemaPtr); });
+    if(!schema) {
+        LogError("Parsing XSD file failed");
+        return ERROR_XSD_PARSE_FAILED;
+    }
+
+
+    std::unique_ptr<xmlSchemaValidCtxt, void(*)(xmlSchemaValidCtxtPtr)>
+        validCtxt(xmlSchemaNewValidCtxt(schema.get()),
+                       [](xmlSchemaValidCtxtPtr validCtxPtr){ xmlSchemaFreeValidCtxt(validCtxPtr); });
+    if(!validCtxt) {
+        LogError("Internal parser error");
+        return ERROR_INTERNAL;
+    }
+
+    xmlSetStructuredErrorFunc(NULL, NULL);
+    xmlSetGenericErrorFunc(this, &Parser::ErrorValidate);
+    xmlThrDefSetStructuredErrorFunc(NULL, NULL);
+    xmlThrDefSetGenericErrorFunc(this, &Parser::ErrorValidate);
+
+    retCode = xmlSchemaValidateFile(validCtxt.get(), m_XMLfile.c_str(), 0);
+    if(0 != retCode) {
+        LogWarning("Validating XML file failed, ec: " << retCode);
+        retCode = ERROR_XML_VALIDATION_FAILED;
+    }
+    else
+        retCode = SUCCESS;
+
+    return retCode;
+}
+
+int Parser::Parse()
+{
+    if(m_elementListenerMap.empty()) {
+        LogError("Can not parse XML file: no registered element callbacks.");
+        return ERROR_INVALID_ARGUMENT;
+    }
+    int retCode = xmlSAXUserParseFile(&m_saxHandler, this, m_XMLfile.c_str());
+    if(0 != retCode) {
+        LogWarning("Parsing XML file failed, ec: " << retCode);
+        return ERROR_XML_PARSE_FAILED;
+    }
+    // if error detected while parsing
+    if(m_elementListenerMap.empty()) {
+        LogError("Critical error detected while parsing.");
+        return ERROR_INTERNAL;
+    }
+    return SUCCESS;
+}
+
+int Parser::RegisterErrorCb(const ErrorCb newCb)
+{
+    if(m_errorCb) {
+        LogError("Callback already registered!");
+        return ERROR_CALLBACK_PRESENT;
+    }
+    m_errorCb = newCb;
+    return SUCCESS;
+}
+
+int Parser::RegisterElementCb(const char * elementName,
+                              const StartCb startCb,
+                              const EndCb endCb)
+{
+    if(!elementName)
+        return ERROR_INVALID_ARGUMENT;
+
+    std::string key(elementName);
+
+    if(m_elementListenerMap.find(elementName) != m_elementListenerMap.end()) {
+        LogError("Callback for element " << elementName << " already registered!");
+        return ERROR_CALLBACK_PRESENT;
+    }
+
+    m_elementListenerMap[key] = {startCb, endCb};
+    return SUCCESS;
+}
+
+void Parser::StartElement(const xmlChar *name,
+                          const xmlChar **attrs)
+{
+    std::string key(reinterpret_cast<const char*>(name));
+    if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
+        return;
+
+    ElementHandlerPtr newHandler;
+    const ElementListener & current = m_elementListenerMap[key];
+    if(current.startCb)
+    {
+        Attributes attribs;
+        {
+            size_t numAttrs = 0;
+            std::string key;
+            while(attrs && attrs[numAttrs])
+            {
+                const char *attrChr = reinterpret_cast<const char*>(attrs[numAttrs]);
+                if((numAttrs%2)==0)
+                    key = std::string(attrChr);
+                else
+                    attribs[key] = std::string(attrChr);
+                numAttrs ++;
+            }
+        }
+
+        newHandler = current.startCb();
+        if(newHandler)
+            newHandler->Start(attribs);
+    }
+    // always put a handler, even if it's empty. This will not break
+    // the sequence of queued elements when popping from the queue.
+    m_elementHandlerStack.push(newHandler);
+}
+
+void Parser::EndElement(const xmlChar *name)
+{
+    std::string key(reinterpret_cast<const char*>(name));
+    if(m_elementListenerMap.find(key) == m_elementListenerMap.end())
+        return;
+
+    // this should never ever happen
+    if( m_elementHandlerStack.empty() )
+        throw std::runtime_error("internal error: element queue desynchronized!");
+
+    ElementHandlerPtr &currentHandler = m_elementHandlerStack.top();
+    if(currentHandler)
+        currentHandler.get()->End();
+
+    const ElementListener & current = m_elementListenerMap[key];
+    if(current.endCb)
+        current.endCb(currentHandler);
+
+    m_elementHandlerStack.pop();
+}
+
+void Parser::Characters(const xmlChar *ch, size_t chLen)
+{
+    std::string chars = trim(std::string(reinterpret_cast<const char*>(ch), chLen));
+    if(chars.empty())
+        return;
+
+    if( !m_elementHandlerStack.empty() )
+    {
+        ElementHandlerPtr &currentHandler = m_elementHandlerStack.top();
+        if(currentHandler)
+            currentHandler.get()->Characters(chars);
+    }
+}
+
+void Parser::Error(const ErrorType errorType, const char *msg, va_list &args)
+{
+    if(!m_errorCb)
+        return;
+
+    va_list args2;
+    try
+    {
+        va_copy(args2, args);
+        std::vector<char> buf(1 + std::vsnprintf(NULL, 0, msg, args));
+        std::vsnprintf(buf.data(), buf.size(), msg, args2);
+        m_errorCb(errorType, trim(std::string(buf.begin(), buf.end())));
+    }
+    catch(...) {
+        LogError("Error callback throwed an exception.");
+        // if an error handler throwed exception,
+        // do not call further callbacks
+        m_elementListenerMap.clear();
+    }
+    va_end(args2);
+}
+
+//
+// -------------------------- start of static wrappers --------------------------
+//
+void Parser::CallbackHelper(std::function<void (void)> func)
+{
+    try
+    {
+        func();
+        return;
+    }
+    catch(const std::exception &e) {
+        LogError("parser error: " << e.what());
+        if(m_errorCb)
+            m_errorCb(PARSE_ERROR, e.what());
+    }
+    catch(...) {
+        LogError("unknown parser error");
+        if(m_errorCb)
+            m_errorCb(PARSE_ERROR, "unknown parser error");
+    }
+    // raise error flag - unregister listeners
+    m_elementListenerMap.clear();
+}
+void Parser::StartElement(void *userData,
+                          const xmlChar *name,
+                          const xmlChar **attrs)
+{
+    Parser *parser = static_cast<Parser *>(userData);
+    parser->CallbackHelper([&parser, &name, &attrs] { parser->StartElement(name, attrs); });
+}
+void Parser::EndElement(void *userData,
+                        const xmlChar *name)
+{
+    Parser *parser = static_cast<Parser *>(userData);
+    parser->CallbackHelper([&parser, &name] { parser->EndElement(name); });
+}
+void Parser::Characters(void *userData,
+                        const xmlChar *ch,
+                        int len)
+{
+    Parser *parser = static_cast<Parser *>(userData);
+    parser->CallbackHelper([&parser, &ch, &len] { parser->Characters(ch, static_cast<size_t>(len)); });
+}
+
+void Parser::ErrorValidate(void *userData,
+                           const char *msg,
+                           ...)
+{
+    va_list args;
+    va_start(args, msg);
+    Parser *parser = static_cast<Parser *>(userData);
+    parser->Error(VALIDATION_ERROR, msg, args);
+    va_end(args);
+}
+
+void Parser::Error(void *userData,
+                   const char *msg,
+                   ...)
+{
+    va_list args;
+    va_start(args, msg);
+    Parser *parser = static_cast<Parser *>(userData);
+    parser->Error(PARSE_ERROR, msg, args);
+    va_end(args);
+}
+
+void Parser::Warning(void *userData,
+                     const char *msg,
+                     ...)
+{
+    va_list args;
+    va_start(args, msg);
+    Parser &parser = *(static_cast<Parser *>(userData));
+    parser.Error(PARSE_WARNING, msg, args);
+    va_end(args);
+}
+//
+// -------------------------- end of static wrappers --------------------------
+//
diff --git a/src/manager/initial-values/parser.h b/src/manager/initial-values/parser.h
new file mode 100644 (file)
index 0000000..01790e8
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ *  Copyright (c) 2000 - 2015 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        parser.h
+ * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version     1.0
+ * @brief       XML parser class.
+ */
+
+#ifndef XML_PARSER_H_
+#define XML_PARSER_H_
+
+#include <map>
+#include <vector>
+#include <string>
+#include <stack>
+#include <functional>
+#include <memory>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+namespace XML
+{
+
+class Parser
+{
+public:
+    enum ErrorCode {
+        SUCCESS                        =   0,
+        ERROR_UNKNOWN                  =   -1000,
+        ERROR_XML_VALIDATION_FAILED    =   -1001,
+        ERROR_XSD_PARSE_FAILED         =   -1002,
+        ERROR_XML_PARSE_FAILED         =   -1003,
+        ERROR_INVALID_ARGUMENT         =   -1004,
+        ERROR_CALLBACK_PRESENT         =   -1005,
+        ERROR_INTERNAL                 =   -1006,
+        ERROR_NO_MEMORY                =   -1007
+    };
+
+    explicit Parser(const char *XML_filename);
+    virtual ~Parser();
+
+    int Validate(const char *XSD_schema);
+    int Parse();
+
+    enum ErrorType {
+        VALIDATION_ERROR,
+        PARSE_ERROR,
+        PARSE_WARNING
+    };
+    typedef std::function<void (const ErrorType, const std::string &)> ErrorCb;
+    int RegisterErrorCb(const ErrorCb newCb);
+
+    typedef std::map<std::string, std::string> Attributes;
+    class ElementHandler
+    {
+        public:
+            virtual ~ElementHandler() {}
+
+            // methods below may throw std::exception to invalidate the parsing process
+            // and remove all element listeners.
+            // In this case, parsing error code returned to the user after std::exception.
+            virtual void Start(const Attributes &) = 0;
+            virtual void Characters(const std::string & data) = 0;
+            virtual void End() = 0;
+    };
+    typedef std::shared_ptr<ElementHandler> ElementHandlerPtr;
+
+    typedef std::function<ElementHandlerPtr ()> StartCb;
+    typedef std::function<void (const ElementHandlerPtr &)> EndCb;
+    int RegisterElementCb(const char * elementName,
+                          const StartCb startCb,
+                          const EndCb endCb);
+
+protected:
+    void StartElement(const xmlChar *name,
+                      const xmlChar **attrs);
+    void EndElement(const xmlChar *name);
+    void Characters(const xmlChar *ch, size_t chLen);
+    void Error(const ErrorType errorType, const char *msg, va_list &);
+
+private:
+    static void StartElement(void *userData,
+                             const xmlChar *name,
+                             const xmlChar **attrs);
+    static void EndElement(void *userData,
+                           const xmlChar *name);
+    static void Characters(void *userData,
+                           const xmlChar *ch,
+                           int len);
+    static void ErrorValidate(void *userData,
+                              const char *msg,
+                              ...);
+    static void Error(void *userData,
+                      const char *msg,
+                      ...);
+    static void Warning(void *userData,
+                        const char *msg,
+                        ...);
+
+private:
+    xmlSAXHandler           m_saxHandler;
+    std::string             m_XMLfile;
+    ErrorCb                 m_errorCb;
+
+    struct ElementListener
+    {
+        StartCb     startCb;
+        EndCb       endCb;
+    };
+    std::map<std::string, ElementListener> m_elementListenerMap;
+    std::stack<ElementHandlerPtr> m_elementHandlerStack;
+
+    void CallbackHelper(std::function<void (void)> func);
+};
+
+}
+#endif /* XML_PARSER_H_ */
index 8c2b2b9..42530c5 100644 (file)
@@ -1,3 +1,8 @@
+PKG_CHECK_MODULES(KEY_MANAGER_TEST_DEP
+    libxml-2.0
+    REQUIRED
+    )
+
 FIND_PACKAGE(Threads REQUIRED)
 ADD_DEFINITIONS( "-DBOOST_TEST_DYN_LINK" )
 
@@ -10,11 +15,13 @@ SET(KEY_MANAGER_PATH ${PROJECT_SOURCE_DIR}/src/manager)
 SET(KEY_MANAGER_TEST_MERGED_SRC ${PROJECT_SOURCE_DIR}/tests)
 
 INCLUDE_DIRECTORIES(
+    ${KEY_MANAGER_DEP_INCLUDE_DIRS}
     ${KEY_MANAGER_PATH}/dpl/db/include
     ${KEY_MANAGER_PATH}/dpl/core/include
     ${KEY_MANAGER_PATH}/dpl/log/include
     ${KEY_MANAGER_PATH}/sqlcipher
     ${KEY_MANAGER_PATH}/service
+    ${KEY_MANAGER_PATH}/initial-values
     ${KEY_MANAGER_PATH}/main
     ${KEY_MANAGER_PATH}/common/
     ${KEY_MANAGER_PATH}/client-async/
@@ -34,8 +41,10 @@ SET(TEST_MERGED_SOURCES
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_descriptor-set.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_comm-manager.cpp
     ${KEY_MANAGER_TEST_MERGED_SRC}/test_serialization.cpp
+    ${KEY_MANAGER_TEST_MERGED_SRC}/test_xml-parser.cpp
     ${KEY_MANAGER_PATH}/service/db-crypto.cpp
     ${KEY_MANAGER_PATH}/service/key-provider.cpp
+    ${KEY_MANAGER_PATH}/initial-values/parser.cpp
     ${KEY_MANAGER_PATH}/client-async/descriptor-set.cpp
     ${KEY_MANAGER_PATH}/dpl/core/src/assert.cpp
     ${KEY_MANAGER_PATH}/dpl/core/src/colors.cpp
@@ -49,6 +58,7 @@ ADD_EXECUTABLE(${TARGET_TEST_MERGED} ${TEST_MERGED_SOURCES})
 TARGET_LINK_LIBRARIES(${TARGET_TEST_MERGED}
     ${TARGET_KEY_MANAGER_COMMON}
     ${CMAKE_THREAD_LIBS_INIT}
+    ${KEY_MANAGER_DEP_LIBRARIES}
     boost_unit_test_framework
     -ldl
     )
diff --git a/tests/XML_1_okay.xml b/tests/XML_1_okay.xml
new file mode 100644 (file)
index 0000000..dae9fcf
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<InitialValues version="0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="initial_values.xsd ">
+  <Key name="key1" type="RSA_PRV" password="123">
+    <PEM>
+      -----BEGIN PUBLIC KEY-----
+      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2b1bXDa+S8/MGWnMkru4
+      T4tUddtZNi0NVjQn9RFH1NMa220GsRhRO56F77FlSVFKfSfVZKIiWg6C+DVCkcLf
+      zXJ/Z0pvwOQYBAqVMFjV6efQGN0JzJ1Unu7pPRiZl7RKGEI+cyzzrcDyrLLrQ2W7
+      0ZySkNEOv6Frx9JgC5NExuYY4lk2fQQa38JXiZkfyzif2em0px7mXbyf5LjccsKq
+      v1e+XLtMsL0ZefRcqsP++NzQAI8fKX7WBT+qK0HJDLiHrKOTWYzx6CwJ66LD/vvf
+      j55xtsKDLVDbsotvf8/m6VLMab+vqKk11TP4tq6yo0mwyTADvgl1zowQEO9I1W6o
+      zQIDAQAB
+      -----END PUBLIC KEY-----
+    </PEM>
+  </Key>
+  <Cert exportable="true" name="cert1">
+    <DER>
+      MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+      BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
+      cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwNTIyMTEyOTQyWhcNMTQwODIwMDAwMDAw
+      WjBtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
+      TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEcMBoGA1UEAwwTYWNj
+      rHva8A==
+    </DER>
+  </Cert>
+  <Data name="data1">
+    <ASCII>
+      My secret data
+    </ASCII>
+  </Data>
+  <Key name="aes1" type="AES">
+    <Base64>
+      MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+    </Base64>
+    <Permission accessor="web_app1"/>
+    <Permission accessor="web_app2"/>
+  </Key>
+</InitialValues>
diff --git a/tests/XML_1_okay.xsd b/tests/XML_1_okay.xsd
new file mode 100644 (file)
index 0000000..a587beb
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+       <xsd:element name="InitialValues" type="InitialValuesType"></xsd:element>
+
+       <xsd:complexType name="InitialValuesType">
+               <xsd:sequence>
+                       <xsd:choice maxOccurs="unbounded" minOccurs="1">
+                               <xsd:element name="Data" type="DataType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                               <xsd:element name="Key" type="KeyType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                               <xsd:element name="Cert" type="CertType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                       </xsd:choice>
+               </xsd:sequence>
+               <xsd:attribute name="version" type="xsd:int" use="required"></xsd:attribute>
+       </xsd:complexType>
+
+       <xsd:complexType name="ObjectType">
+               <xsd:attribute name="name" use="required">
+                       <xsd:simpleType>
+                               <xsd:restriction base="xsd:string">
+                                       <xsd:minLength value="1"></xsd:minLength>
+                               </xsd:restriction>
+                       </xsd:simpleType>
+               </xsd:attribute>
+               <xsd:attribute name="password" use="optional"
+                       type="xsd:string">
+               </xsd:attribute>
+               <xsd:attribute name="exportable" use="optional"
+                       default="false">
+                       <xsd:simpleType>
+                               <xsd:restriction base="xsd:boolean"></xsd:restriction>
+                       </xsd:simpleType>
+               </xsd:attribute>
+       </xsd:complexType>
+
+       <xsd:complexType name="KeyType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="PEM"
+                                                       type="EncodingPemType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="DER"
+                                                       type="EncodingDerType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="Base64"
+                                                       type="EncodingRawType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                               <xsd:attribute name="type">
+                                       <xsd:simpleType>
+                                               <xsd:restriction base="xsd:string">
+                                                       <xsd:enumeration value="RSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="RSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="ECDSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="ECDSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="DSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="DSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="AES"></xsd:enumeration>
+                                               </xsd:restriction>
+                                       </xsd:simpleType>
+                               </xsd:attribute>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:complexType name="CertType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="PEM" type="EncodingPemType"
+                                                       maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="DER"
+                                                       type="EncodingDerType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:complexType name="DataType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="ASCII"
+                                                       type="EncodingAsciiType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="Base64"
+                                                       type="EncodingRawType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:simpleType name="EncodingAsciiType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingDerType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingPemType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingRawType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+
+       <xsd:complexType name="PermissionType">
+               <xsd:attribute name="accessor" type="xsd:string"></xsd:attribute>
+       </xsd:complexType>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/tests/XML_1_wrong.xml b/tests/XML_1_wrong.xml
new file mode 100644 (file)
index 0000000..72a565a
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<InitialValues version="0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="initial_values.xsd ">
+  <Key name="key1" type="RSA_PRV" password="123">
+    <PEM>
+      -----BEGIN PUBLIC KEY-----
+      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2b1bXDa+S8/MGWnMkru4
+      T4tUddtZNi0NVjQn9RFH1NMa220GsRhRO56F77FlSVFKfSfVZKIiWg6C+DVCkcLf
+      zXJ/Z0pvwOQYBAqVMFjV6efQGN0JzJ1Unu7pPRiZl7RKGEI+cyzzrcDyrLLrQ2W7
+      0ZySkNEOv6Frx9JgC5NExuYY4lk2fQQa38JXiZkfyzif2em0px7mXbyf5LjccsKq
+      v1e+XLtMsL0ZefRcqsP++NzQAI8fKX7WBT+qK0HJDLiHrKOTWYzx6CwJ66LD/vvf
+      j55xtsKDLVDbsotvf8/m6VLMab+vqKk11TP4tq6yo0mwyTADvgl1zowQEO9I1W6o
+      zQIDAQAB
+      -----END PUBLIC KEY-----
+    </PEM>
+  </HERE_GOES_AN_ERROR>
+  <Cert exportable="true" name="cert1">
+    <DER>
+      MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+      BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
+      cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwNTIyMTEyOTQyWhcNMTQwODIwMDAwMDAw
+      WjBtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
+      TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEcMBoGA1UEAwwTYWNj
+      rHva8A==
+    </DER>
+  </Cert>
+  <Data name="data1">
+    <ASCII>
+      My secret data
+    </ASCII>
+  </Data>
+  <Key name="aes1" type="AES">
+    <Base64>
+      MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+    </Base64>
+    <Permission accessor="web_app1"/>
+    <Permission accessor="web_app2"/>
+  </Key>
+</InitialValues>
diff --git a/tests/XML_1_wrong.xsd b/tests/XML_1_wrong.xsd
new file mode 100644 (file)
index 0000000..558e16a
--- /dev/null
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsd:schema elementFormDefault="qualified" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+       <xsd:element name="InitialValues" type="InitialValuesType"></xsd:element>
+
+       <xsd:complexType name="InitialValuesType">
+               <xsd:sequence>
+                       <xsd:choice maxOccurs="unbounded" minOccurs="1">
+                               <xsd:element name="Data" type="DataType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                               <xsd:element name="Key" type="KeyType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                               <xsd:element name="Cert" type="CertType"
+                                       maxOccurs="1" minOccurs="1">
+                               </xsd:element>
+                       </xsd:choice>
+<!--           </xsd:sequence> -->
+               <xsd:attribute name="version" type="xsd:int" use="required"></xsd:attribute>
+       </xsd:complexType>
+
+       <xsd:complexType name="ObjectType">
+               <xsd:attribute name="name" use="required">
+                       <xsd:simpleType>
+                               <xsd:restriction base="xsd:string">
+                                       <xsd:minLength value="1"></xsd:minLength>
+                               </xsd:restriction>
+                       </xsd:simpleType>
+               </xsd:attribute>
+               <xsd:attribute name="password" use="optional"
+                       type="xsd:string">
+               </xsd:attribute>
+               <xsd:attribute name="exportable" use="optional"
+                       default="false">
+                       <xsd:simpleType>
+                               <xsd:restriction base="xsd:boolean"></xsd:restriction>
+                       </xsd:simpleType>
+               </xsd:attribute>
+       </xsd:complexType>
+
+       <xsd:complexType name="KeyType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="PEM"
+                                                       type="EncodingPemType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="DER"
+                                                       type="EncodingDerType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="Base64"
+                                                       type="EncodingRawType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                               <xsd:attribute name="type">
+                                       <xsd:simpleType>
+                                               <xsd:restriction base="xsd:string">
+                                                       <xsd:enumeration value="RSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="RSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="ECDSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="ECDSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="DSA_PRV"></xsd:enumeration>
+                                                       <xsd:enumeration value="DSA_PUB"></xsd:enumeration>
+                                                       <xsd:enumeration value="AES"></xsd:enumeration>
+                                               </xsd:restriction>
+                                       </xsd:simpleType>
+                               </xsd:attribute>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:complexType name="CertType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="PEM" type="EncodingPemType"
+                                                       maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="DER"
+                                                       type="EncodingDerType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:complexType name="DataType">
+               <xsd:complexContent>
+                       <xsd:extension base="ObjectType">
+                               <xsd:sequence>
+                                       <xsd:choice maxOccurs="1" minOccurs="1">
+                                               <xsd:element name="ASCII"
+                                                       type="EncodingAsciiType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                               <xsd:element name="Base64"
+                                                       type="EncodingRawType" maxOccurs="1" minOccurs="1">
+                                               </xsd:element>
+                                       </xsd:choice>
+                                       <xsd:element name="Permission" type="PermissionType" maxOccurs="unbounded" minOccurs="0"></xsd:element>
+                               </xsd:sequence>
+                       </xsd:extension>
+               </xsd:complexContent>
+       </xsd:complexType>
+
+       <xsd:simpleType name="EncodingAsciiType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingDerType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingPemType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+       <xsd:simpleType name="EncodingRawType">
+               <xsd:restriction base="xsd:string"></xsd:restriction>
+       </xsd:simpleType>
+
+       <xsd:complexType name="PermissionType">
+               <xsd:attribute name="accessor" type="xsd:string"></xsd:attribute>
+       </xsd:complexType>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/tests/XML_2_structure.xml b/tests/XML_2_structure.xml
new file mode 100644 (file)
index 0000000..02538c4
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<StructureTest version="0">
+<Add>1</Add>
+<Add powerFactor="4">2</Add>
+<Add>3</Add>
+<Add>5</Add>
+<Add dummyAttr="123">7</Add>
+<Add>11</Add>
+<Add>13</Add>
+<Multiply>
+    -10
+</Multiply>
+<Add>17</Add>
+<Add>19</Add>
+<Div>
+    
+    
+    -2</Div>
+<ExpectedSum>
+                        262
+</ExpectedSum>
+</StructureTest>
diff --git a/tests/test_xml-parser.cpp b/tests/test_xml-parser.cpp
new file mode 100644 (file)
index 0000000..04bc72e
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ *  Copyright (c) 2000 - 2015 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        test_xml-parser.cpp
+ * @author      Maciej Karpiuk (m.karpiuk2@samsung.com)
+ * @version     1.0
+ * @brief       XML parser tests.
+ */
+
+#include <vector>
+#include <boost/test/unit_test.hpp>
+#include <parser.h>
+
+using namespace XML;
+
+namespace
+{
+const char *XML_1_okay          = "XML_1_okay.xml";
+const char *XSD_1_okay          = "XML_1_okay.xsd";
+const char *XML_1_wrong         = "XML_1_wrong.xml";
+const char *XSD_1_wrong         = "XML_1_wrong.xsd";
+const char *XML_2_structure     = "XML_2_structure.xml";
+const char *XML_3_structure     = "XML_3_structure.xml";
+
+std::string format_test_path(const char *file)
+{
+    return std::string("/usr/share/ckm-db-test/") + std::string(file);
+}
+
+bool startCallbackFlag = false;
+XML::Parser::ElementHandlerPtr dummyStartCallback()
+{
+    startCallbackFlag = true;
+    // return empty pointer
+    return XML::Parser::ElementHandlerPtr();
+}
+bool endCallbackFlag = false;
+void dummyEndCallback(const XML::Parser::ElementHandlerPtr &)
+{
+    endCallbackFlag = true;
+}
+}
+
+BOOST_AUTO_TEST_SUITE(XML_PARSER_TEST)
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_wrong_argument)
+{
+    XML::Parser parser(0);
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_INVALID_ARGUMENT == parser.Validate(0));
+
+    // no listeners
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_INVALID_ARGUMENT == parser.Parse());
+
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.RegisterElementCb("Key", dummyStartCallback, dummyEndCallback));
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_XML_PARSE_FAILED == parser.Parse());
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_no_XML_file)
+{
+    XML::Parser parser(format_test_path("i-am-not-here").c_str());
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_XML_VALIDATION_FAILED == parser.Validate(format_test_path(XSD_1_okay).c_str()));
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML1_correct_verify)
+{
+    XML::Parser parser(format_test_path(XML_1_okay).c_str());
+    BOOST_REQUIRE(0 == parser.Validate(format_test_path(XSD_1_okay).c_str()));
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML1_wrong_verify)
+{
+    XML::Parser parser(format_test_path(XML_1_wrong).c_str());
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_XML_VALIDATION_FAILED == parser.Validate(format_test_path(XSD_1_okay).c_str()));
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML1_wrong_schema)
+{
+    XML::Parser parser(format_test_path(XML_1_okay).c_str());
+    BOOST_REQUIRE(Parser::ErrorCode::ERROR_XSD_PARSE_FAILED == parser.Validate(format_test_path(XSD_1_wrong).c_str()));
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML1_correct_parse_incorrect_callbacks)
+{
+    XML::Parser parser(format_test_path(XML_1_okay).c_str());
+    BOOST_REQUIRE(0 == parser.Validate(format_test_path(XSD_1_okay).c_str()));
+
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.RegisterElementCb("Data", NULL, NULL));
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.Parse());
+}
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML1_correct_parse)
+{
+    XML::Parser parser(format_test_path(XML_1_okay).c_str());
+    BOOST_REQUIRE(0 == parser.Validate(format_test_path(XSD_1_okay).c_str()));
+
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.RegisterElementCb("Key", dummyStartCallback, NULL));
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.RegisterElementCb("Cert", NULL, dummyEndCallback));
+    startCallbackFlag = false;
+    endCallbackFlag = false;
+    BOOST_REQUIRE(Parser::ErrorCode::SUCCESS == parser.Parse());
+    BOOST_REQUIRE(startCallbackFlag == true);
+    BOOST_REQUIRE(endCallbackFlag == true);
+}
+
+
+class StructureTest
+{
+public:
+    class ExpectedSumHandler : public XML::Parser::ElementHandler
+    {
+        public:
+            ExpectedSumHandler() : m_value(0) {}
+
+            virtual void Start(const XML::Parser::Attributes &) {}
+            virtual void Characters(const std::string &data) {
+                m_value = atoi(data.c_str());
+            }
+            virtual void End() {}
+
+            int getSum() const {
+                return m_value;
+            }
+
+        protected:
+            int m_value;
+    };
+
+    class MathHandler : public XML::Parser::ElementHandler
+    {
+        public:
+            MathHandler() : m_valueSet(false), m_powerFactor(1) {}
+
+            virtual void Start(const XML::Parser::Attributes &attr) {
+                const auto & it = attr.find("powerFactor");
+                if(it != attr.end())
+                    m_powerFactor = atoi(it->second.c_str());
+            }
+            virtual void Characters(const std::string &data) {
+                m_value = pow(atoi(data.c_str()), m_powerFactor);
+                m_valueSet = true;
+            }
+            virtual void End() {}
+
+            virtual int compute(int prevVal) = 0;
+
+        protected:
+            bool m_valueSet;
+            int m_value;
+            int m_powerFactor;
+    };
+    class AddHandler : public MathHandler
+    {
+        public:
+            virtual int compute(int prevVal) {
+                if( !m_valueSet )
+                    return prevVal;
+
+                return prevVal + m_value;
+            }
+    };
+
+    class MultiplyHandler : public MathHandler
+    {
+        public:
+            virtual int compute(int prevVal) {
+                if( !m_valueSet )
+                    return prevVal;
+
+                return prevVal * m_value;
+            }
+    };
+
+    class DivHandler : public MathHandler
+    {
+        public:
+            virtual int compute(int prevVal) {
+                if( !m_valueSet )
+                    return prevVal;
+
+                if(m_value == 0)
+                    return prevVal;
+                return prevVal / m_value;
+            }
+    };
+
+    StructureTest(const char *filename) : m_parser(filename), m_sum(0), m_expectedSum(0)
+    {
+        m_parser.RegisterErrorCb(StructureTest::Error);
+        m_parser.RegisterElementCb("Add",
+                [this]() -> XML::Parser::ElementHandlerPtr
+                {
+                    return std::make_shared<AddHandler>();
+                },
+                [this](const XML::Parser::ElementHandlerPtr & element)
+                {
+                    // add computation
+                    if(element)
+                    {
+                        MathHandler *mathElement = reinterpret_cast<MathHandler*>(element.get());
+                        m_sum = mathElement->compute(m_sum);
+                    }
+                });
+        m_parser.RegisterElementCb("Multiply",
+                [this]() -> XML::Parser::ElementHandlerPtr
+                {
+                    return std::make_shared<MultiplyHandler>();
+                },
+                [this](const XML::Parser::ElementHandlerPtr &element)
+                {
+                    // multiply computation
+                    if(element)
+                    {
+                        MathHandler *mathElement = reinterpret_cast<MathHandler*>(element.get());
+                        m_sum = mathElement->compute(m_sum);
+                    }
+                });
+        m_parser.RegisterElementCb("Div",
+                [this]() -> XML::Parser::ElementHandlerPtr
+                {
+                    return std::make_shared<DivHandler>();
+                },
+                [this](const XML::Parser::ElementHandlerPtr &element)
+                {
+                    // division computation
+                    if(element)
+                    {
+                        MathHandler *mathElement = reinterpret_cast<MathHandler*>(element.get());
+                        m_sum = mathElement->compute(m_sum);
+                    }
+                });
+        m_parser.RegisterElementCb("ExpectedSum",
+                [this]() -> XML::Parser::ElementHandlerPtr
+                {
+                    return std::make_shared<ExpectedSumHandler>();
+                },
+                [this](const XML::Parser::ElementHandlerPtr &element)
+                {
+                    if(element)
+                    {
+                        ExpectedSumHandler *sumElement = reinterpret_cast<ExpectedSumHandler*>(element.get());
+                        m_expectedSum = sumElement->getSum();
+                    }
+                });
+    }
+
+    static void Error(const Parser::ErrorType /*errorType*/,
+                      const std::string & log_msg)
+    {
+        BOOST_FAIL(log_msg);
+    }
+
+    int Parse()
+    {
+        return m_parser.Parse();
+    }
+
+    int getSum() const {
+        return m_sum;
+    }
+    int getExpectedSum() const {
+        return m_expectedSum;
+    }
+private:
+    XML::Parser m_parser;
+    int m_sum;
+    int m_expectedSum;
+};
+
+BOOST_AUTO_TEST_CASE(XmlParserTest_XML2_structure)
+{
+    StructureTest parser(format_test_path(XML_2_structure).c_str());
+    BOOST_REQUIRE(0 == parser.Parse());
+    BOOST_REQUIRE_MESSAGE(parser.getSum() == parser.getExpectedSum(),
+                          "got sum: " << parser.getSum() << " while expected: " << parser.getExpectedSum());
+}
+
+BOOST_AUTO_TEST_SUITE_END()