From 1b344808ff4d92c611d6120bf7b52dde64fa2263 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Fri, 3 May 2013 20:18:01 +0000 Subject: [PATCH] added more unit tests and did some refactoring of xml parser code --- source/shared_lib/include/xml/xml_parser.h | 35 ++-- source/shared_lib/sources/xml/xml_parser.cpp | 85 +++++++--- .../tests/shared_lib/xml/xml_parser_test.cpp | 155 +++++++++++++++++- 3 files changed, 228 insertions(+), 47 deletions(-) diff --git a/source/shared_lib/include/xml/xml_parser.h b/source/shared_lib/include/xml/xml_parser.h index 7f6531c02..8cd8887bd 100644 --- a/source/shared_lib/include/xml/xml_parser.h +++ b/source/shared_lib/include/xml/xml_parser.h @@ -59,16 +59,21 @@ class XmlAttribute; /// Wrapper for Xerces C++ // ===================================================== -class XmlIo{ +class XmlIo { private: static bool initialized; XERCES_CPP_NAMESPACE::DOMImplementation *implementation; - -private: - XmlIo(); - void init(); +#if XERCES_VERSION_MAJOR < 3 + XERCES_CPP_NAMESPACE::DOMBuilder *parser; +#else + XERCES_CPP_NAMESPACE::DOMLSParser *parser; +#endif protected: + XmlIo(); + virtual ~XmlIo(); + + void init(); #if XERCES_VERSION_MAJOR < 3 XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * getRootDOMDocument(const string &path, DOMBuilder *parser, bool noValidation); @@ -76,14 +81,16 @@ protected: XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * getRootDOMDocument(const string &path, DOMLSParser *parser, bool noValidation); #endif + DOMNode *loadDOMNode(const string &path, bool noValidation=false); + virtual void releaseDOMParser(); + public: static XmlIo &getInstance(); - ~XmlIo(); - void cleanup(); static bool isInitialized(); + void cleanup(); - XmlNode *load(const string &path, const std::map &mapTagReplacementValues,bool noValidation=false, bool skipStackCheck=false); + XmlNode *load(const string &path, const std::map &mapTagReplacementValues,bool noValidation=false); void save(const string &path, const XmlNode *node); }; @@ -99,9 +106,9 @@ private: public: static XmlIoRapid &getInstance(); ~XmlIoRapid(); - void cleanup(); static bool isInitialized(); + void cleanup(); XmlNode *load(const string &path, const std::map &mapTagReplacementValues,bool noValidation=false,bool skipStackCheck=false); void save(const string &path, const XmlNode *node); @@ -149,6 +156,9 @@ private: XmlNode(XmlNode&); void operator =(XmlNode&); + string getTreeString() const; + bool hasChildNoSuper(const string& childName) const; + public: XmlNode(XERCES_CPP_NAMESPACE::DOMNode *node, const std::map &mapTagReplacementValues); XmlNode(xml_node<> *node, const std::map &mapTagReplacementValues); @@ -172,18 +182,13 @@ public: bool hasChildAtIndex(const string &childName, int childIndex=0) const; bool hasChild(const string &childName) const; int clearChild(const string &childName); - XmlNode *getParent() const; - XmlNode *addChild(const string &name); + XmlNode *addChild(const string &name, const string text = ""); XmlAttribute *addAttribute(const string &name, const string &value, const std::map &mapTagReplacementValues); XERCES_CPP_NAMESPACE::DOMElement *buildElement(XERCES_CPP_NAMESPACE::DOMDocument *document) const; xml_node<>* buildElement(xml_document<> *document) const; - -private: - string getTreeString() const; - bool hasChildNoSuper(const string& childName) const; }; // ===================================================== diff --git a/source/shared_lib/sources/xml/xml_parser.cpp b/source/shared_lib/sources/xml/xml_parser.cpp index 7c53181d4..697070e31 100644 --- a/source/shared_lib/sources/xml/xml_parser.cpp +++ b/source/shared_lib/sources/xml/xml_parser.cpp @@ -64,10 +64,14 @@ public: bool XmlIo::initialized = false; bool XmlIoRapid::initialized= false; -XmlIo::XmlIo() { +XmlIo::XmlIo() : parser(NULL) { init(); } +XmlIo::~XmlIo() { + cleanup(); +} + bool XmlIo::isInitialized() { return XmlIo::initialized; } @@ -105,6 +109,7 @@ XmlIo &XmlIo::getInstance() { } void XmlIo::cleanup() { + releaseDOMParser(); if(XmlIo::initialized == true) { XmlIo::initialized= false; //printf("XmlIo cleanup\n"); @@ -112,10 +117,6 @@ void XmlIo::cleanup() { } } -XmlIo::~XmlIo() { - cleanup(); -} - #if XERCES_VERSION_MAJOR < 3 XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument * XmlIo::getRootDOMDocument(const string &path, DOMBuilder *parser, bool noValidation) { XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *document= parser->parseURI(path.c_str()); @@ -128,7 +129,14 @@ XmlIo::~XmlIo() { } #endif -XmlNode *XmlIo::load(const string &path, const std::map &mapTagReplacementValues,bool noValidation,bool skipStackCheck) { +void XmlIo::releaseDOMParser() { + if(parser != NULL) { + parser->release(); + parser = NULL; + } +} + +DOMNode *XmlIo::loadDOMNode(const string &path, bool noValidation) { //printf("Load file using Xerces engine [%s]\n",path.c_str()); try { @@ -136,15 +144,15 @@ XmlNode *XmlIo::load(const string &path, const std::map &mapTagRe ErrorHandler errorHandler; #if XERCES_VERSION_MAJOR < 3 - DOMBuilder *parser= (static_cast(implementation))->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); - parser->setErrorHandler(&errorHandler); - if(noValidation == false) { + parser= (static_cast(implementation))->createDOMBuilder(DOMImplementationLS::MODE_SYNCHRONOUS, 0); + parser->setErrorHandler(&errorHandler); + if(noValidation == false) { parser->setFeature(XMLUni::fgXercesSchema, true); parser->setFeature(XMLUni::fgXercesSchemaFullChecking, true); //parser->setFeature(XMLUni::fgDOMValidateIfSchema, true); parser->setFeature(XMLUni::fgDOMValidation, true); - } - else { + } + else { //parser->setFeature(XMLUni::fgSAX2CoreValidation, false); //parser->setFeature(XMLUni::fgXercesDynamic, true); @@ -153,30 +161,30 @@ XmlNode *XmlIo::load(const string &path, const std::map &mapTagRe //parser->setFeature(XMLUni::fgDOMValidateIfSchema, true); //parser->setFeature(XMLUni::fgDOMValidation, false); - parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false); - parser->setFeature(XMLUni::fgXercesLoadExternalDTD, false); - parser->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true); - parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true); - } + parser->setFeature(XMLUni::fgXercesSchemaFullChecking, false); + parser->setFeature(XMLUni::fgXercesLoadExternalDTD, false); + parser->setFeature(XMLUni::fgXercesCacheGrammarFromParse, true); + parser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true); + } #else - DOMLSParser *parser = (static_cast(implementation))->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0); - DOMConfiguration *config = parser->getDomConfig(); - if(noValidation == false) { + parser = (static_cast(implementation))->createLSParser(DOMImplementationLS::MODE_SYNCHRONOUS, 0); + DOMConfiguration *config = parser->getDomConfig(); + if(noValidation == false) { config->setParameter(XMLUni::fgXercesSchema, true); config->setParameter(XMLUni::fgXercesSchemaFullChecking, true); //config->setParameter(XMLUni::fgDOMValidateIfSchema, true); config->setParameter(XMLUni::fgDOMValidate, true); - } - else { + } + else { config->setParameter(XMLUni::fgXercesSchema, false); config->setParameter(XMLUni::fgXercesSchemaFullChecking, false); config->setParameter(XMLUni::fgXercesLoadExternalDTD, false); config->setParameter(XMLUni::fgXercesCacheGrammarFromParse, true); config->setParameter(XMLUni::fgXercesUseCachedGrammarInParse, true); - } + } #endif //XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *document= parser->parseURI(path.c_str()); - XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *document = getRootDOMDocument(path, parser, noValidation); + XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument *document = getRootDOMDocument(path, parser, noValidation); #ifdef WIN32 if(document == NULL) { @@ -187,8 +195,29 @@ XmlNode *XmlIo::load(const string &path, const std::map &mapTagRe throw megaglest_runtime_error("Can not parse URL: " + path); } - XmlNode *rootNode= new XmlNode(document->getDocumentElement(),mapTagReplacementValues); - parser->release(); + DOMNode *rootNode = document->getDocumentElement(); + return rootNode; + } + catch(const DOMException &ex) { + char szBuf[8096]=""; + snprintf(szBuf,8096,"In [%s::%s Line: %d] Exception while loading: [%s], msg:\n%s",extractFileFromDirectoryPath(__FILE__).c_str(),__FUNCTION__,__LINE__,path.c_str(),XMLString::transcode(ex.msg)); + SystemFlags::OutputDebug(SystemFlags::debugError,"%s\n",szBuf); + + throw megaglest_runtime_error(szBuf); + } + return NULL; +} + +XmlNode *XmlIo::load(const string &path, const std::map &mapTagReplacementValues,bool noValidation) { + //printf("Load file using Xerces engine [%s]\n",path.c_str()); + + try { + if(SystemFlags::VERBOSE_MODE_ENABLED) printf("XERCES_FULLVERSIONDOT [%s]\nnoValidation = %d\npath [%s]\n",XERCES_FULLVERSIONDOT,noValidation,path.c_str()); + + DOMNode *domNode = loadDOMNode(path, noValidation); + XmlNode *rootNode= new XmlNode(domNode,mapTagReplacementValues); + releaseDOMParser(); + return rootNode; } catch(const DOMException &ex) { @@ -512,7 +541,7 @@ void XmlTree::load(const string &path, const std::map &mapTagRepl loadPath = path; if(this->engine_type == XML_XERCES_ENGINE) { - this->rootNode= XmlIo::getInstance().load(path, mapTagReplacementValues, noValidation,this->skipStackCheck); + this->rootNode= XmlIo::getInstance().load(path, mapTagReplacementValues, noValidation); } else { this->rootNode= XmlIoRapid::getInstance().load(path, mapTagReplacementValues, noValidation,this->skipStackCheck); @@ -745,6 +774,7 @@ bool XmlNode::hasChildAtIndex(const string &childName, int i) const { return superNode->hasChildAtIndex(childName,i); int count= 0; for(unsigned int j = 0; j < children.size(); ++j) { + //printf("Looking for [%s] at index: %d found [%s] index = %d\n",childName.c_str(),i,children[j]->getName().c_str(),j); if(children[j]->getName()==childName) { if(count == i) { return true; @@ -770,9 +800,10 @@ bool XmlNode::hasChildNoSuper(const string &childName) const { return false; } -XmlNode *XmlNode::addChild(const string &name){ +XmlNode *XmlNode::addChild(const string &name, const string text) { assert(!superNode); XmlNode *node= new XmlNode(name); + node->text = text; children.push_back(node); return node; } diff --git a/source/tests/shared_lib/xml/xml_parser_test.cpp b/source/tests/shared_lib/xml/xml_parser_test.cpp index f45b75719..2fe721459 100644 --- a/source/tests/shared_lib/xml/xml_parser_test.cpp +++ b/source/tests/shared_lib/xml/xml_parser_test.cpp @@ -15,6 +15,10 @@ #include "xml_parser.h" #include "platform_util.h" +#include +//#include +//#include + #ifdef WIN32 #include #else @@ -37,7 +41,7 @@ bool removeTestFile(string file) { void createValidXMLTestFile(const string& test_filename) { std::ofstream xmlFile(test_filename.c_str()); xmlFile << "" << std::endl - << "" << std::endl + << "" << std::endl << "" << std::endl << "" << std::endl; xmlFile.close(); @@ -103,7 +107,7 @@ public: XmlNode *rootNode = XmlIo::getInstance().load(test_filename, std::map()); CPPUNIT_ASSERT( rootNode != NULL ); - CPPUNIT_ASSERT( rootNode->getName() == "menu" ); + CPPUNIT_ASSERT_EQUAL( string("menu"), rootNode->getName() ); } void test_load_file_malformed_content() { const string test_filename = "xml_test_malformed.xml"; @@ -170,7 +174,7 @@ public: XmlNode *rootNode = XmlIoRapid::getInstance().load(test_filename, std::map()); CPPUNIT_ASSERT( rootNode != NULL ); - CPPUNIT_ASSERT( rootNode->getName() == "menu" ); + CPPUNIT_ASSERT_EQUAL( string("menu"), rootNode->getName() ); } void test_load_file_malformed_content() { const string test_filename = "xml_test_malformed.xml"; @@ -233,11 +237,11 @@ public: XmlTree xmlInstance; xmlInstance.init(""); CPPUNIT_ASSERT( xmlInstance.getRootNode() != NULL ); - CPPUNIT_ASSERT( xmlInstance.getRootNode()->getName() == "" ); + CPPUNIT_ASSERT_EQUAL( string(""), xmlInstance.getRootNode()->getName() ); xmlInstance.init("testRoot"); CPPUNIT_ASSERT( xmlInstance.getRootNode() != NULL ); - CPPUNIT_ASSERT( xmlInstance.getRootNode()->getName() == "testRoot" ); + CPPUNIT_ASSERT_EQUAL( string("testRoot"), xmlInstance.getRootNode()->getName() ); } void test_load_simultaneously_same_file() { const string test_filename = "xml_test_valid.xml"; @@ -264,7 +268,148 @@ public: } }; + +class XmlNodeTest : public CppUnit::TestFixture { + // Register the suite of tests for this fixture + CPPUNIT_TEST_SUITE( XmlNodeTest ); + + CPPUNIT_TEST_EXCEPTION( test_null_xerces_node, megaglest_runtime_error ); + CPPUNIT_TEST_EXCEPTION( test_null_rapidxml_node, megaglest_runtime_error ); + CPPUNIT_TEST( test_valid_xerces_node ); + CPPUNIT_TEST( test_valid_named_node ); + CPPUNIT_TEST( test_child_nodes ); + CPPUNIT_TEST( test_node_attributes ); + + CPPUNIT_TEST_SUITE_END(); + // End of Fixture registration + +private: + + class XmlIoMock : public XmlIo { + protected: + virtual void releaseDOMParser() { } + + public: + XmlIoMock() : XmlIo() { } + + DOMNode *loadDOMNode(const string &path, bool noValidation=false) { + return XmlIo::loadDOMNode(path, noValidation); + } + + void manualParserRelease() { + XmlIo::releaseDOMParser(); + } + }; + +public: + + void test_null_xerces_node() { + XERCES_CPP_NAMESPACE::DOMNode *node = NULL; + const std::map mapTagReplacementValues; + XmlNode(node, mapTagReplacementValues); + } + void test_null_rapidxml_node() { + xml_node<> *node = NULL; + const std::map mapTagReplacementValues; + XmlNode(node, mapTagReplacementValues); + } + void test_valid_xerces_node() { + const string test_filename = "xml_test_valid.xml"; + createValidXMLTestFile(test_filename); + SafeRemoveTestFile deleteFile(test_filename); + + XmlIoMock xml; + XERCES_CPP_NAMESPACE::DOMNode *domNode = xml.loadDOMNode(test_filename); + + CPPUNIT_ASSERT( domNode != NULL ); + + const std::map mapTagReplacementValues; + XmlNode node(domNode, mapTagReplacementValues); + + xml.manualParserRelease(); + + CPPUNIT_ASSERT_EQUAL( string("menu"), node.getName() ); + CPPUNIT_ASSERT( node.hasAttribute("mytest-attribute") == true ); + CPPUNIT_ASSERT( node.hasChild("menu-background-model") == true ); + } + void test_valid_named_node() { + XmlNode node("testNode"); + + CPPUNIT_ASSERT_EQUAL( string("testNode"), node.getName() ); + } + + void test_child_nodes() { + XmlNode node("testNode"); + + CPPUNIT_ASSERT( node.getName() == "testNode" ); + CPPUNIT_ASSERT_EQUAL( (size_t)0,node.getChildCount() ); + + XmlNode *childNode1 = node.addChild("child1"); + CPPUNIT_ASSERT_EQUAL( (size_t)1,node.getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string(""), childNode1->getText() ); + + XmlNode *childChildNode1 = childNode1->addChild("childchild1", "testValue"); + CPPUNIT_ASSERT_EQUAL( (size_t)1,childNode1->getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string("testValue"), childChildNode1->getText() ); + + XmlNode *childChildNode2 = childNode1->addChild("childchild2", "testValue2"); + CPPUNIT_ASSERT_EQUAL( (size_t)2, childNode1->getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string("testValue2"), childChildNode2->getText() ); + + XmlNode *childChildNode3 = childNode1->addChild("childchild2", "testValue3"); + CPPUNIT_ASSERT_EQUAL( (size_t)3, childNode1->getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string("testValue3"), childChildNode3->getText() ); + + CPPUNIT_ASSERT( childNode1->hasChildAtIndex("childchild2",1) == true); + + XmlNode *childNode2 = node.addChild("child2","child2Value"); + CPPUNIT_ASSERT_EQUAL( (size_t)2,node.getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string("child2Value"), childNode2->getText() ); + + CPPUNIT_ASSERT_EQUAL( string("child2"), node.getChild(1)->getName() ); + CPPUNIT_ASSERT_EQUAL( string("child2"), node.getChild("child2")->getName() ); + CPPUNIT_ASSERT_EQUAL( string("child1"), node.getChild("child1")->getName() ); + + XmlNode *childNode2x = node.addChild("child2","child2xValue"); + CPPUNIT_ASSERT_EQUAL( (size_t)3, node.getChildCount() ); + CPPUNIT_ASSERT_EQUAL( string("child2xValue"), childNode2x->getText() ); + CPPUNIT_ASSERT_EQUAL( string("child2xValue"), node.getChild("child2",1)->getText() ); + + XmlNode *childNode3 = node.addChild("child3","child3Value"); + CPPUNIT_ASSERT_EQUAL( (size_t)4, node.getChildCount() ); + + vector child2List = node.getChildList("child2"); + CPPUNIT_ASSERT_EQUAL( (size_t)2, child2List.size() ); + CPPUNIT_ASSERT_EQUAL( string("child2Value"), child2List[0]->getText() ); + CPPUNIT_ASSERT_EQUAL( string("child2xValue"), child2List[1]->getText() ); + + //printf("%d\n",__LINE__); + CPPUNIT_ASSERT( childNode3->hasChild("child2") == false); + CPPUNIT_ASSERT_EQUAL( 2, node.clearChild("child2")); + CPPUNIT_ASSERT_EQUAL( (size_t)2,node.getChildCount() ); + } + + void test_node_attributes() { + XmlNode node("testNode"); + + CPPUNIT_ASSERT( node.getName() == "testNode" ); + CPPUNIT_ASSERT_EQUAL( (size_t)0,node.getAttributeCount() ); + CPPUNIT_ASSERT_EQUAL( (XmlAttribute *)NULL, node.getAttribute("some-attribute",false) ); + CPPUNIT_ASSERT_EQUAL( false, node.hasAttribute("some-attribute") ); + + std::map mapTagReplacementValues; + XmlAttribute *attribute1 = node.addAttribute("some-attribute", "some-value", mapTagReplacementValues); + CPPUNIT_ASSERT_EQUAL( (size_t)1,node.getAttributeCount() ); + CPPUNIT_ASSERT_EQUAL( attribute1, node.getAttribute("some-attribute") ); + CPPUNIT_ASSERT_EQUAL( string("some-attribute"), node.getAttribute(0)->getName() ); + CPPUNIT_ASSERT_EQUAL( true, node.hasAttribute("some-attribute") ); + } + +}; + + // Suite Registrations CPPUNIT_TEST_SUITE_REGISTRATION( XmlIoTest ); CPPUNIT_TEST_SUITE_REGISTRATION( XmlIoRapidTest ); CPPUNIT_TEST_SUITE_REGISTRATION( XmlTreeTest ); +CPPUNIT_TEST_SUITE_REGISTRATION( XmlNodeTest );