From: Maciej J. Karpiuk Date: Wed, 6 May 2015 13:20:41 +0000 (+0200) Subject: Add generic XML parser + tests. X-Git-Tag: accepted/tizen/mobile/20150629.000431~18 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fsecurity%2Fkey-manager.git;a=commitdiff_plain;h=82f657827c1c162b72e454784daf92103ed315a4 Add generic XML parser + tests. Change-Id: I44494b0e3034cb0e6e258bc9b8da8cadb5e2be70 --- diff --git a/packaging/key-manager.spec b/packaging/key-manager.spec index 3916e13..7855988 100644 --- a/packaging/key-manager.spec +++ b/packaging/key-manager.spec @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92635be..ce6680a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 index 0000000..06f8143 --- /dev/null +++ b/src/manager/initial-values/parser.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +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 + parserCtxt(xmlSchemaNewParserCtxt(XSD_schema), + [](xmlSchemaParserCtxtPtr ctx){ xmlSchemaFreeParserCtxt(ctx); }); + if(!parserCtxt) { + LogError("XSD file path is invalid"); + return ERROR_INVALID_ARGUMENT; + } + + std::unique_ptr + schema(xmlSchemaParse(parserCtxt.get()), + [](xmlSchemaPtr schemaPtr){ xmlSchemaFree(schemaPtr); }); + if(!schema) { + LogError("Parsing XSD file failed"); + return ERROR_XSD_PARSE_FAILED; + } + + + std::unique_ptr + 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(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(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(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 ¤tHandler = 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(ch), chLen)); + if(chars.empty()) + return; + + if( !m_elementHandlerStack.empty() ) + { + ElementHandlerPtr ¤tHandler = 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 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 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(userData); + parser->CallbackHelper([&parser, &name, &attrs] { parser->StartElement(name, attrs); }); +} +void Parser::EndElement(void *userData, + const xmlChar *name) +{ + Parser *parser = static_cast(userData); + parser->CallbackHelper([&parser, &name] { parser->EndElement(name); }); +} +void Parser::Characters(void *userData, + const xmlChar *ch, + int len) +{ + Parser *parser = static_cast(userData); + parser->CallbackHelper([&parser, &ch, &len] { parser->Characters(ch, static_cast(len)); }); +} + +void Parser::ErrorValidate(void *userData, + const char *msg, + ...) +{ + va_list args; + va_start(args, msg); + Parser *parser = static_cast(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(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(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 index 0000000..01790e8 --- /dev/null +++ b/src/manager/initial-values/parser.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 ErrorCb; + int RegisterErrorCb(const ErrorCb newCb); + + typedef std::map 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 ElementHandlerPtr; + + typedef std::function StartCb; + typedef std::function 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 m_elementListenerMap; + std::stack m_elementHandlerStack; + + void CallbackHelper(std::function func); +}; + +} +#endif /* XML_PARSER_H_ */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 8c2b2b9..42530c5 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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 index 0000000..dae9fcf --- /dev/null +++ b/tests/XML_1_okay.xml @@ -0,0 +1,38 @@ + + + + + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2b1bXDa+S8/MGWnMkru4 + T4tUddtZNi0NVjQn9RFH1NMa220GsRhRO56F77FlSVFKfSfVZKIiWg6C+DVCkcLf + zXJ/Z0pvwOQYBAqVMFjV6efQGN0JzJ1Unu7pPRiZl7RKGEI+cyzzrcDyrLLrQ2W7 + 0ZySkNEOv6Frx9JgC5NExuYY4lk2fQQa38JXiZkfyzif2em0px7mXbyf5LjccsKq + v1e+XLtMsL0ZefRcqsP++NzQAI8fKX7WBT+qK0HJDLiHrKOTWYzx6CwJ66LD/vvf + j55xtsKDLVDbsotvf8/m6VLMab+vqKk11TP4tq6yo0mwyTADvgl1zowQEO9I1W6o + zQIDAQAB + -----END PUBLIC KEY----- + + + + + MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE + BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl + cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwNTIyMTEyOTQyWhcNMTQwODIwMDAwMDAw + WjBtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN + TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEcMBoGA1UEAwwTYWNj + rHva8A== + + + + + My secret data + + + + + MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE + + + + + diff --git a/tests/XML_1_okay.xsd b/tests/XML_1_okay.xsd new file mode 100644 index 0000000..a587beb --- /dev/null +++ b/tests/XML_1_okay.xsd @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/XML_1_wrong.xml b/tests/XML_1_wrong.xml new file mode 100644 index 0000000..72a565a --- /dev/null +++ b/tests/XML_1_wrong.xml @@ -0,0 +1,38 @@ + + + + + -----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2b1bXDa+S8/MGWnMkru4 + T4tUddtZNi0NVjQn9RFH1NMa220GsRhRO56F77FlSVFKfSfVZKIiWg6C+DVCkcLf + zXJ/Z0pvwOQYBAqVMFjV6efQGN0JzJ1Unu7pPRiZl7RKGEI+cyzzrcDyrLLrQ2W7 + 0ZySkNEOv6Frx9JgC5NExuYY4lk2fQQa38JXiZkfyzif2em0px7mXbyf5LjccsKq + v1e+XLtMsL0ZefRcqsP++NzQAI8fKX7WBT+qK0HJDLiHrKOTWYzx6CwJ66LD/vvf + j55xtsKDLVDbsotvf8/m6VLMab+vqKk11TP4tq6yo0mwyTADvgl1zowQEO9I1W6o + zQIDAQAB + -----END PUBLIC KEY----- + + + + + MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE + BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl + cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwNTIyMTEyOTQyWhcNMTQwODIwMDAwMDAw + WjBtMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN + TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEcMBoGA1UEAwwTYWNj + rHva8A== + + + + + My secret data + + + + + MIIEgDCCA2igAwIBAgIIcjtBYJGQtOAwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE + + + + + diff --git a/tests/XML_1_wrong.xsd b/tests/XML_1_wrong.xsd new file mode 100644 index 0000000..558e16a --- /dev/null +++ b/tests/XML_1_wrong.xsd @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/XML_2_structure.xml b/tests/XML_2_structure.xml new file mode 100644 index 0000000..02538c4 --- /dev/null +++ b/tests/XML_2_structure.xml @@ -0,0 +1,22 @@ + + +1 +2 +3 +5 +7 +11 +13 + + -10 + +17 +19 +
+ + + -2
+ + 262 + +
diff --git a/tests/test_xml-parser.cpp b/tests/test_xml-parser.cpp new file mode 100644 index 0000000..04bc72e --- /dev/null +++ b/tests/test_xml-parser.cpp @@ -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 +#include +#include + +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(); + }, + [this](const XML::Parser::ElementHandlerPtr & element) + { + // add computation + if(element) + { + MathHandler *mathElement = reinterpret_cast(element.get()); + m_sum = mathElement->compute(m_sum); + } + }); + m_parser.RegisterElementCb("Multiply", + [this]() -> XML::Parser::ElementHandlerPtr + { + return std::make_shared(); + }, + [this](const XML::Parser::ElementHandlerPtr &element) + { + // multiply computation + if(element) + { + MathHandler *mathElement = reinterpret_cast(element.get()); + m_sum = mathElement->compute(m_sum); + } + }); + m_parser.RegisterElementCb("Div", + [this]() -> XML::Parser::ElementHandlerPtr + { + return std::make_shared(); + }, + [this](const XML::Parser::ElementHandlerPtr &element) + { + // division computation + if(element) + { + MathHandler *mathElement = reinterpret_cast(element.get()); + m_sum = mathElement->compute(m_sum); + } + }); + m_parser.RegisterElementCb("ExpectedSum", + [this]() -> XML::Parser::ElementHandlerPtr + { + return std::make_shared(); + }, + [this](const XML::Parser::ElementHandlerPtr &element) + { + if(element) + { + ExpectedSumHandler *sumElement = reinterpret_cast(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()