PageRenderTime 226ms CodeModel.GetById 205ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/mordor/tests/xml.cpp

http://github.com/mozy/mordor
C++ | 311 lines | 262 code | 39 blank | 10 comment | 14 complexity | c050bf4a8aa465c8895547cecd2ec73e MD5 | raw file
  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 == "&amp;")
 24        value.append("&");
 25    else if (string == "&gt;")
 26        value.append(">");
 27    else if (string == "&lt;")
 28        value.append("<");
 29    else if (string == "&quot;")
 30        value.append("\"");
 31    else if (string == "&apos;")
 32        value.append("\'");
 33    else
 34        // Real code should also look for character references like "&#38;"
 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, "&amp;", "&");
 47    replace(rawstring, "&gt;", ">" );
 48    replace(rawstring, "&lt;", "<" );
 49    replace(rawstring, "&quot;", "\"");
 50    replace(rawstring, "&apos;", "\'");
 51    // Should also look for character references like "&#38;"
 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&amp;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/&lt;C:\\abc\\n&apos;&amp;.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>&quot;&amp;</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=\"&quot;mymail&apos;\"/>");
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=\'&quot;\"\' 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:&quot;mymail&quot;\" 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>  &quot;some &amp; some&quot;  </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>&#x394;&#x3BF;&#x3BA;&#x3B9;&#x3BC;&#x3AE;&#931;</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(), "&#x394;&#x3BF;&#x3BA;&#x3B9;&#x3BC;&#x3AE;&#931;");
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