/mordor/tests/xml.cpp
C++ | 311 lines | 262 code | 39 blank | 10 comment | 14 complexity | c050bf4a8aa465c8895547cecd2ec73e MD5 | raw file
Possible License(s): BSD-3-Clause
1// Copyright (c) 2010 - Mozy, Inc. 2 3#include <boost/bind.hpp> 4 5#include "mordor/xml/dom_parser.h" 6#include "mordor/test/test.h" 7#include "mordor/string.h" 8 9using namespace Mordor; 10 11static void callback(std::string &value, int &called, 12 const std::string &string) 13{ 14 value.append(string); 15 ++called; 16} 17 18static void reference(std::string &value, int &called, 19 const std::string &string) 20{ 21 // When processing inner text a special callback is invoked for xml 22 // references 23 if (string == "&") 24 value.append("&"); 25 else if (string == ">") 26 value.append(">"); 27 else if (string == "<") 28 value.append("<"); 29 else if (string == """) 30 value.append("\""); 31 else if (string == "'") 32 value.append("\'"); 33 else 34 // Real code should also look for character references like "&" 35 MORDOR_NOTREACHED(); 36 37 ++called; 38} 39 40static void handlereferencecallback(std::string &value, int &called, 41 const std::string &string) 42{ 43 // A full Attribute value is passed, which may contain xml references 44 std::string rawstring(string); 45 46 replace(rawstring, "&", "&"); 47 replace(rawstring, ">", ">" ); 48 replace(rawstring, "<", "<" ); 49 replace(rawstring, """, "\""); 50 replace(rawstring, "'", "\'"); 51 // Should also look for character references like "&" 52 53 value.append(rawstring); 54 ++called; 55} 56 57 58static void emptyTag(int &called) 59{ 60 ++called; 61} 62 63MORDOR_UNITTEST(XMLParser, basic) 64{ 65 std::string start, end; 66 int calledStart = 0, calledEnd = 0; 67 CallbackXMLParserEventHandler handler( 68 boost::bind(&callback, boost::ref(start), boost::ref(calledStart), _1), 69 boost::bind(&callback, boost::ref(end), boost::ref(calledEnd), _1)); 70 XMLParser parser(handler); 71 parser.run("<body></body>"); 72 MORDOR_ASSERT(parser.final()); 73 MORDOR_ASSERT(!parser.error()); 74 MORDOR_TEST_ASSERT_EQUAL(calledStart, 1); 75 MORDOR_TEST_ASSERT_EQUAL(start, "body"); 76 MORDOR_TEST_ASSERT_EQUAL(calledEnd, 1); 77 MORDOR_TEST_ASSERT_EQUAL(end, "body"); 78} 79 80MORDOR_UNITTEST(XMLParser, emptyTag) 81{ 82 std::string tag; 83 int calledStart = 0, calledEmpty = 0; 84 CallbackXMLParserEventHandler handler( 85 boost::bind(&callback, boost::ref(tag), boost::ref(calledStart), _1), 86 NULL, 87 boost::bind(&emptyTag, boost::ref(calledEmpty))); 88 XMLParser parser(handler); 89 parser.run("<empty />"); 90 MORDOR_ASSERT(parser.final()); 91 MORDOR_ASSERT(!parser.error()); 92 MORDOR_TEST_ASSERT_EQUAL(calledStart, 1); 93 MORDOR_TEST_ASSERT_EQUAL(tag, "empty"); 94 MORDOR_TEST_ASSERT_EQUAL(calledEmpty, 1); 95} 96 97MORDOR_UNITTEST(XMLParser, references) 98{ 99 std::string text; 100 int called = 0; 101 CallbackXMLParserEventHandler handler(NULL, 102 NULL, 103 NULL, 104 NULL, 105 NULL, 106 boost::bind(&callback, boost::ref(text), boost::ref(called), _1), 107 boost::bind(&reference, boost::ref(text), boost::ref(called), _1)); 108 XMLParser parser(handler); 109 parser.run("<root>sometext&somemoretext</root>"); 110 MORDOR_ASSERT(parser.final()); 111 MORDOR_ASSERT(!parser.error()); 112 MORDOR_TEST_ASSERT_EQUAL(called, 3); 113 MORDOR_TEST_ASSERT_EQUAL(text, "sometext&somemoretext"); 114 115 text.clear(); 116 called = 0; 117 parser.run("<path>/Users/test1/Public/<C:\\abc\\n'&.txt1</path>"); 118 MORDOR_ASSERT(parser.final()); 119 MORDOR_ASSERT(!parser.error()); 120 MORDOR_TEST_ASSERT_EQUAL(called, 6); 121 MORDOR_TEST_ASSERT_EQUAL(text, "/Users/test1/Public/<C:\\abc\\n'&.txt1"); 122 123 text.clear(); 124 called = 0; 125 parser.run("<p>"&</p>"); 126 MORDOR_ASSERT(parser.final()); 127 MORDOR_ASSERT(!parser.error()); 128 MORDOR_TEST_ASSERT_EQUAL(called, 2); 129 MORDOR_TEST_ASSERT_EQUAL(text, "\"&"); 130} 131 132MORDOR_UNITTEST(XMLParser, attribute) 133{ 134 std::string key, value; 135 int calledKey = 0, calledVal = 0; 136 CallbackXMLParserEventHandler handler( 137 NULL, 138 NULL, 139 NULL, 140 boost::bind(&callback, boost::ref(key), boost::ref(calledKey), _1), 141 boost::bind(&handlereferencecallback, boost::ref(value), boost::ref(calledVal), _1)); 142 XMLParser parser(handler); 143 144 parser.run("<mykey query=\"mymail\"/>"); 145 MORDOR_ASSERT(parser.final()); 146 MORDOR_ASSERT(!parser.error()); 147 MORDOR_TEST_ASSERT_EQUAL(calledKey, 1); 148 MORDOR_TEST_ASSERT_EQUAL(key, "query"); 149 MORDOR_TEST_ASSERT_EQUAL(calledVal, 1); 150 MORDOR_TEST_ASSERT_EQUAL(value, "mymail"); 151 152 key.clear(); value.clear(); 153 calledKey = 0; calledVal = 0; 154 parser.run("<mykey qry=\""mymail'\"/>"); 155 MORDOR_ASSERT(parser.final()); 156 MORDOR_ASSERT(!parser.error()); 157 MORDOR_TEST_ASSERT_EQUAL(calledKey, 1); 158 MORDOR_TEST_ASSERT_EQUAL(key, "qry"); 159 MORDOR_TEST_ASSERT_EQUAL(calledVal, 1); 160 MORDOR_TEST_ASSERT_EQUAL(value, "\"mymail\'"); 161 162 key.clear(); value.clear(); 163 calledKey = 0; calledVal = 0; 164 parser.run("<mykey a=\'"\"\' b=\"foo's\"/>"); 165 MORDOR_ASSERT(parser.final()); 166 MORDOR_ASSERT(!parser.error()); 167 MORDOR_TEST_ASSERT_EQUAL(calledKey, 2); 168 MORDOR_TEST_ASSERT_EQUAL(key, "ab"); // The test callback concatenates them together 169 MORDOR_TEST_ASSERT_EQUAL(calledVal, 2); 170 MORDOR_TEST_ASSERT_EQUAL(value, "\"\"foo's"); 171 172 // A more complex real life XML 173 key.clear(); value.clear(); 174 parser.run("<folder id=\"3\" i4ms=\"692002\" ms=\"456229\" name=\"Trash\" n=\"38\" l=\"1\"><search id=\"384010\" sortBy=\"dateDesc\" query=\"(to:(foo) tag:"mymail"\" name=\"inbox my mail\" l=\"3\" /></folder>"); 175 MORDOR_ASSERT(parser.final()); 176 MORDOR_ASSERT(!parser.error()); 177 MORDOR_ASSERT(!key.empty()); 178 MORDOR_ASSERT(!value.empty()); 179} 180 181const char * xml_typical = 182"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\t" 183"<NamedObject type='file' deleted='false'>\n" 184" <id>/sync/1/path/x.y.txt</id>" 185" <versionId>1310496040</versionId>" 186"\t<Version deleted='false'>" 187"\t\t<versionId>1310496040</versionId>" 188"<size>8</size>" 189"<stime>Tue, 12 Jul 2011 18:40:40 GMT</stime>\r\n" 190"<fileAttributes archive='true' not_content_indexed='true' />" 191"</Version>" 192"</NamedObject>"; 193 194MORDOR_UNITTEST(XMLParser, parsedocument) 195{ 196 // Confirm that a full xml with random white space and 197 // typical elements can be parsed. 198 XMLParserEventHandler defaulthandler; 199 XMLParser parser(defaulthandler); 200 parser.run(xml_typical); 201 MORDOR_ASSERT(parser.final()); 202 MORDOR_ASSERT(!parser.error()); 203} 204 205MORDOR_UNITTEST(XMLParser, domParser) 206{ 207 DOM::XMLParser parser; 208 DOM::Document::ptr doc = parser.loadDocument( 209 "<?xml version='1.0' encoding='UTF-8'?>\n" 210 "<root><children>\n" 211 "<child> this is child1\n</child>\n" 212 "<child>this is child2</child>\n" 213 "<empty id=\"1\" attr1=\"one\" attr2='two' />\n" 214 "<tag>tag text<subtag> "some & some" </subtag></tag>\n" 215 "</children></root>"); 216 217 DOM::Element *root = (DOM::Element *)doc->getElementsByTagName("root")[0]; 218 219 MORDOR_TEST_ASSERT_EQUAL("root", root->nodeName()); 220 DOM::NodeList l = root->getElementsByTagName("child"); 221 MORDOR_TEST_ASSERT_EQUAL(2u, l.size()); 222 MORDOR_TEST_ASSERT_EQUAL("this is child1", l[0]->text()); 223 MORDOR_TEST_ASSERT_EQUAL("this is child1", l[0]->firstChild()->nodeValue()); 224 MORDOR_TEST_ASSERT_EQUAL("this is child2", l[1]->text()); 225 226 DOM::Node *children = root->firstChild(); 227 MORDOR_TEST_ASSERT_EQUAL(DOM::ELEMENT, children->nodeType()); 228 MORDOR_TEST_ASSERT_EQUAL("children", children->nodeName()); 229 MORDOR_TEST_ASSERT_EQUAL(4u, children->childNodes().size()); 230 231 DOM::Element *empty = (DOM::Element *)children->childNodes()[2]; 232 MORDOR_TEST_ASSERT_EQUAL("empty", empty->nodeName()); 233 MORDOR_TEST_ASSERT_EQUAL(DOM::ELEMENT, empty->nodeType()); 234 MORDOR_TEST_ASSERT_EQUAL("", empty->text()); 235 MORDOR_TEST_ASSERT_EQUAL("1", empty->id); 236 MORDOR_TEST_ASSERT_EQUAL("one", empty->attribute("attr1")); 237 MORDOR_TEST_ASSERT_EQUAL("two", empty->attribute("attr2")); 238 MORDOR_TEST_ASSERT_EQUAL(true, empty->hasAttribute("attr2")); 239 MORDOR_TEST_ASSERT_EQUAL(false, empty->hasAttribute("attr3")); 240 241 DOM::Element *node = doc->getElementById("1"); 242 MORDOR_TEST_ASSERT_EQUAL(empty, node); 243 MORDOR_TEST_ASSERT_EQUAL((DOM::Element *)NULL, doc->getElementById("haha")); 244 245 DOM::Node *tag = children->childNodes()[3]; 246 MORDOR_TEST_ASSERT_EQUAL(DOM::ELEMENT, tag->nodeType()); 247 MORDOR_TEST_ASSERT_EQUAL("tag", tag->nodeName()); 248 MORDOR_TEST_ASSERT_EQUAL("tag text", tag->text()); 249 MORDOR_TEST_ASSERT_EQUAL("tag text", tag->firstChild()->nodeValue()); 250 251 DOM::NodeList subtags = ((DOM::Element *)tag)->getElementsByTagName("subtag"); 252 MORDOR_TEST_ASSERT_EQUAL(1u, subtags.size()); 253 MORDOR_TEST_ASSERT_EQUAL("\"some & some\"", subtags[0]->text()); 254} 255 256MORDOR_UNITTEST(XMLParser, domParserInvalid) 257{ 258 DOM::XMLParser parser; 259 MORDOR_TEST_ASSERT_EXCEPTION(parser.loadDocument( 260 "<?xml version='1.0' encoding='UTF-8'?>\n" 261 "<root<children>\"somesome\n"), 262 std::invalid_argument); 263} 264 265MORDOR_UNITTEST(XMLParser, domParserUnmatchedEndTag) 266{ 267 DOM::XMLParser parser; 268 MORDOR_TEST_ASSERT_EXCEPTION(parser.loadDocument( 269 "<?xml version='1.0' encoding='UTF-8'?>\n" 270 "<hello></goodbye>"), 271 std::invalid_argument); 272 MORDOR_TEST_ASSERT_EXCEPTION(parser.loadDocument( 273 "<?xml version='1.0' encoding='UTF-8'?>\n" 274 "<hello>\n" 275 " <bonjour>\n" 276 " </ciao>\n" 277 "</goodbye>"), 278 std::invalid_argument); 279} 280 281MORDOR_UNITTEST(XMLParser, domParserReferenceError) 282{ 283 const std::string xmlstr = 284 "<Object>ΔοκιμήΣ</Object>"; 285 DOM::XMLParser parser; 286 DOM::Document::ptr doc = parser.loadDocument(xmlstr); 287 DOM::Node *obj = doc->getElementsByTagName("Object")[0]; 288 MORDOR_TEST_ASSERT_EQUAL(obj->text(), "ΔοκιμήΣ"); 289} 290 291#if 0 292// Disabled until comment support fixed 293MORDOR_UNITTEST(XMLParser, parsecomment) 294{ 295 XMLParserEventHandler defaulthandler; 296 XMLParser parser(defaulthandler); 297 298 parser.run("<!-- a comment -->"); 299 MORDOR_ASSERT(parser.final()); 300 MORDOR_ASSERT(!parser.error()); 301 302 parser.run("foo <!-- bar --> baz <!-- qox --> blurb"); 303 MORDOR_ASSERT(parser.final()); 304 MORDOR_ASSERT(!parser.error()); 305 306 parser.run("<![CDATA[ <!-- OMGWTFBBQ ]]>Shoulda used a <!-- real parser -->"); 307 MORDOR_ASSERT(parser.final()); 308 MORDOR_ASSERT(!parser.error()); 309} 310#endif 311