1# Copyright (c) Twisted Matrix Laboratories. 2# See LICENSE for details. 3 4""" 5Tests for L{twisted.words.xish.domish}, a DOM-like library for XMPP. 6""" 7 8from twisted.trial import unittest 9from twisted.words.xish import domish 10 11 12class DomishTestCase(unittest.TestCase): 13 def testEscaping(self): 14 s = "&<>'\"" 15 self.assertEqual(domish.escapeToXml(s), "&<>'\"") 16 self.assertEqual(domish.escapeToXml(s, 1), "&<>'"") 17 18 def testNamespaceObject(self): 19 ns = domish.Namespace("testns") 20 self.assertEqual(ns.foo, ("testns", "foo")) 21 22 def testElementInit(self): 23 e = domish.Element((None, "foo")) 24 self.assertEqual(e.name, "foo") 25 self.assertEqual(e.uri, None) 26 self.assertEqual(e.defaultUri, None) 27 self.assertEqual(e.parent, None) 28 29 e = domish.Element(("", "foo")) 30 self.assertEqual(e.name, "foo") 31 self.assertEqual(e.uri, "") 32 self.assertEqual(e.defaultUri, "") 33 self.assertEqual(e.parent, None) 34 35 e = domish.Element(("testns", "foo")) 36 self.assertEqual(e.name, "foo") 37 self.assertEqual(e.uri, "testns") 38 self.assertEqual(e.defaultUri, "testns") 39 self.assertEqual(e.parent, None) 40 41 e = domish.Element(("testns", "foo"), "test2ns") 42 self.assertEqual(e.name, "foo") 43 self.assertEqual(e.uri, "testns") 44 self.assertEqual(e.defaultUri, "test2ns") 45 46 def testChildOps(self): 47 e = domish.Element(("testns", "foo")) 48 e.addContent("somecontent") 49 b2 = e.addElement(("testns2", "bar2")) 50 e["attrib1"] = "value1" 51 e[("testns2", "attrib2")] = "value2" 52 e.addElement("bar") 53 e.addElement("bar") 54 e.addContent("abc") 55 e.addContent("123") 56 57 # Check content merging 58 self.assertEqual(e.children[-1], "abc123") 59 60 # Check str()/content extraction 61 self.assertEqual(str(e), "somecontent") 62 63 # Check direct child accessor 64 self.assertEqual(e.bar2, b2) 65 e.bar2.addContent("subcontent") 66 e.bar2["bar2value"] = "somevalue" 67 68 # Check child ops 69 self.assertEqual(e.children[1], e.bar2) 70 self.assertEqual(e.children[2], e.bar) 71 72 # Check attribute ops 73 self.assertEqual(e["attrib1"], "value1") 74 del e["attrib1"] 75 self.assertEqual(e.hasAttribute("attrib1"), 0) 76 self.assertEqual(e.hasAttribute("attrib2"), 0) 77 self.assertEqual(e[("testns2", "attrib2")], "value2") 78 79 80 def test_elements(self): 81 """ 82 Calling C{elements} without arguments on a L{domish.Element} returns 83 all child elements, whatever the qualfied name. 84 """ 85 e = domish.Element((u"testns", u"foo")) 86 c1 = e.addElement(u"name") 87 c2 = e.addElement((u"testns2", u"baz")) 88 c3 = e.addElement(u"quux") 89 c4 = e.addElement((u"testns", u"name")) 90 91 elts = list(e.elements()) 92 93 self.assertIn(c1, elts) 94 self.assertIn(c2, elts) 95 self.assertIn(c3, elts) 96 self.assertIn(c4, elts) 97 98 99 def test_elementsWithQN(self): 100 """ 101 Calling C{elements} with a namespace and local name on a 102 L{domish.Element} returns all child elements with that qualified name. 103 """ 104 e = domish.Element((u"testns", u"foo")) 105 c1 = e.addElement(u"name") 106 c2 = e.addElement((u"testns2", u"baz")) 107 c3 = e.addElement(u"quux") 108 c4 = e.addElement((u"testns", u"name")) 109 110 elts = list(e.elements(u"testns", u"name")) 111 112 self.assertIn(c1, elts) 113 self.assertNotIn(c2, elts) 114 self.assertNotIn(c3, elts) 115 self.assertIn(c4, elts) 116 117 118 119class DomishStreamTestsMixin: 120 """ 121 Mixin defining tests for different stream implementations. 122 123 @ivar streamClass: A no-argument callable which will be used to create an 124 XML parser which can produce a stream of elements from incremental 125 input. 126 """ 127 def setUp(self): 128 self.doc_started = False 129 self.doc_ended = False 130 self.root = None 131 self.elements = [] 132 self.stream = self.streamClass() 133 self.stream.DocumentStartEvent = self._docStarted 134 self.stream.ElementEvent = self.elements.append 135 self.stream.DocumentEndEvent = self._docEnded 136 137 def _docStarted(self, root): 138 self.root = root 139 self.doc_started = True 140 141 def _docEnded(self): 142 self.doc_ended = True 143 144 def doTest(self, xml): 145 self.stream.parse(xml) 146 147 def testHarness(self): 148 xml = "<root><child/><child2/></root>" 149 self.stream.parse(xml) 150 self.assertEqual(self.doc_started, True) 151 self.assertEqual(self.root.name, 'root') 152 self.assertEqual(self.elements[0].name, 'child') 153 self.assertEqual(self.elements[1].name, 'child2') 154 self.assertEqual(self.doc_ended, True) 155 156 def testBasic(self): 157 xml = "<stream:stream xmlns:stream='etherx' xmlns='jabber'>\n" + \ 158 " <message to='bar'>" + \ 159 " <x xmlns='xdelay'>some&data></x>" + \ 160 " </message>" + \ 161 "</stream:stream>" 162 163 self.stream.parse(xml) 164 self.assertEqual(self.root.name, 'stream') 165 self.assertEqual(self.root.uri, 'etherx') 166 self.assertEqual(self.elements[0].name, 'message') 167 self.assertEqual(self.elements[0].uri, 'jabber') 168 self.assertEqual(self.elements[0]['to'], 'bar') 169 self.assertEqual(self.elements[0].x.uri, 'xdelay') 170 self.assertEqual(unicode(self.elements[0].x), 'some&data>') 171 172 def testNoRootNS(self): 173 xml = "<stream><error xmlns='etherx'/></stream>" 174 175 self.stream.parse(xml) 176 self.assertEqual(self.root.uri, '') 177 self.assertEqual(self.elements[0].uri, 'etherx') 178 179 def testNoDefaultNS(self): 180 xml = "<stream:stream xmlns:stream='etherx'><error/></stream:stream>""" 181 182 self.stream.parse(xml) 183 self.assertEqual(self.root.uri, 'etherx') 184 self.assertEqual(self.root.defaultUri, '') 185 self.assertEqual(self.elements[0].uri, '') 186 self.assertEqual(self.elements[0].defaultUri, '') 187 188 def testChildDefaultNS(self): 189 xml = "<root xmlns='testns'><child/></root>" 190 191 self.stream.parse(xml) 192 self.assertEqual(self.root.uri, 'testns') 193 self.assertEqual(self.elements[0].uri, 'testns') 194 195 def testEmptyChildNS(self): 196 xml = "<root xmlns='testns'><child1><child2 xmlns=''/></child1></root>" 197 198 self.stream.parse(xml) 199 self.assertEqual(self.elements[0].child2.uri, '') 200 201 202 def test_namespaceWithWhitespace(self): 203 """ 204 Whitespace in an xmlns value is preserved in the resulting node's C{uri} 205 attribute. 206 """ 207 xml = "<root xmlns:foo=' bar baz '><foo:bar foo:baz='quux'/></root>" 208 self.stream.parse(xml) 209 self.assertEqual(self.elements[0].uri, " bar baz ") 210 self.assertEqual( 211 self.elements[0].attributes, {(" bar baz ", "baz"): "quux"}) 212 213 214 def testChildPrefix(self): 215 xml = "<root xmlns='testns' xmlns:foo='testns2'><foo:child/></root>" 216 217 self.stream.parse(xml) 218 self.assertEqual(self.root.localPrefixes['foo'], 'testns2') 219 self.assertEqual(self.elements[0].uri, 'testns2') 220 221 def testUnclosedElement(self): 222 self.assertRaises(domish.ParserError, self.stream.parse, 223 "<root><error></root>") 224 225 def test_namespaceReuse(self): 226 """ 227 Test that reuse of namespaces does affect an element's serialization. 228 229 When one element uses a prefix for a certain namespace, this is 230 stored in the C{localPrefixes} attribute of the element. We want 231 to make sure that elements created after such use, won't have this 232 prefix end up in their C{localPrefixes} attribute, too. 233 """ 234 235 xml = """<root> 236 <foo:child1 xmlns:foo='testns'/> 237 <child2 xmlns='testns'/> 238 </root>""" 239 240 self.stream.parse(xml) 241 self.assertEqual('child1', self.elements[0].name) 242 self.assertEqual('testns', self.elements[0].uri) 243 self.assertEqual('', self.elements[0].defaultUri) 244 self.assertEqual({'foo': 'testns'}, self.elements[0].localPrefixes) 245 self.assertEqual('child2', self.elements[1].name) 246 self.assertEqual('testns', self.elements[1].uri) 247 self.assertEqual('testns', self.elements[1].defaultUri) 248 self.assertEqual({}, self.elements[1].localPrefixes) 249 250 251 252class DomishExpatStreamTestCase(DomishStreamTestsMixin, unittest.TestCase): 253 """ 254 Tests for L{domish.ExpatElementStream}, the expat-based element stream 255 implementation. 256 """ 257 streamClass = domish.ExpatElementStream 258 259 try: 260 import pyexpat 261 except ImportError: 262 skip = "pyexpat is required for ExpatElementStream tests." 263 264 265 266class DomishSuxStreamTestCase(DomishStreamTestsMixin, unittest.TestCase): 267 """ 268 Tests for L{domish.SuxElementStream}, the L{twisted.web.sux}-based element 269 stream implementation. 270 """ 271 streamClass = domish.SuxElementStream 272 273 if domish.SuxElementStream is None: 274 skip = "twisted.web is required for SuxElementStream tests." 275 276 277 278class SerializerTests(unittest.TestCase): 279 def testNoNamespace(self): 280 e = domish.Element((None, "foo")) 281 self.assertEqual(e.toXml(), "<foo/>") 282 self.assertEqual(e.toXml(closeElement = 0), "<foo>") 283 284 def testDefaultNamespace(self): 285 e = domish.Element(("testns", "foo")) 286 self.assertEqual(e.toXml(), "<foo xmlns='testns'/>") 287 288 def testOtherNamespace(self): 289 e = domish.Element(("testns", "foo"), "testns2") 290 self.assertEqual(e.toXml({'testns': 'bar'}), 291 "<bar:foo xmlns:bar='testns' xmlns='testns2'/>") 292 293 def testChildDefaultNamespace(self): 294 e = domish.Element(("testns", "foo")) 295 e.addElement("bar") 296 self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>") 297 298 def testChildSameNamespace(self): 299 e = domish.Element(("testns", "foo")) 300 e.addElement(("testns", "bar")) 301 self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>") 302 303 def testChildSameDefaultNamespace(self): 304 e = domish.Element(("testns", "foo")) 305 e.addElement("bar", "testns") 306 self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>") 307 308 def testChildOtherDefaultNamespace(self): 309 e = domish.Element(("testns", "foo")) 310 e.addElement(("testns2", "bar"), 'testns2') 311 self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar xmlns='testns2'/></foo>") 312 313 def testOnlyChildDefaultNamespace(self): 314 e = domish.Element((None, "foo")) 315 e.addElement(("ns2", "bar"), 'ns2') 316 self.assertEqual(e.toXml(), "<foo><bar xmlns='ns2'/></foo>") 317 318 def testOnlyChildDefaultNamespace2(self): 319 e = domish.Element((None, "foo")) 320 e.addElement("bar") 321 self.assertEqual(e.toXml(), "<foo><bar/></foo>") 322 323 def testChildInDefaultNamespace(self): 324 e = domish.Element(("testns", "foo"), "testns2") 325 e.addElement(("testns2", "bar")) 326 self.assertEqual(e.toXml(), "<xn0:foo xmlns:xn0='testns' xmlns='testns2'><bar/></xn0:foo>") 327 328 def testQualifiedAttribute(self): 329 e = domish.Element((None, "foo"), 330 attribs = {("testns2", "bar"): "baz"}) 331 self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'/>") 332 333 def testQualifiedAttributeDefaultNS(self): 334 e = domish.Element(("testns", "foo"), 335 attribs = {("testns", "bar"): "baz"}) 336 self.assertEqual(e.toXml(), "<foo xmlns='testns' xmlns:xn0='testns' xn0:bar='baz'/>") 337 338 def testTwoChilds(self): 339 e = domish.Element(('', "foo")) 340 child1 = e.addElement(("testns", "bar"), "testns2") 341 child1.addElement(('testns2', 'quux')) 342 child2 = e.addElement(("testns3", "baz"), "testns4") 343 child2.addElement(('testns', 'quux')) 344 self.assertEqual(e.toXml(), "<foo><xn0:bar xmlns:xn0='testns' xmlns='testns2'><quux/></xn0:bar><xn1:baz xmlns:xn1='testns3' xmlns='testns4'><xn0:quux xmlns:xn0='testns'/></xn1:baz></foo>") 345 346 def testXMLNamespace(self): 347 e = domish.Element((None, "foo"), 348 attribs = {("http://www.w3.org/XML/1998/namespace", 349 "lang"): "en_US"}) 350 self.assertEqual(e.toXml(), "<foo xml:lang='en_US'/>") 351 352 def testQualifiedAttributeGivenListOfPrefixes(self): 353 e = domish.Element((None, "foo"), 354 attribs = {("testns2", "bar"): "baz"}) 355 self.assertEqual(e.toXml({"testns2": "qux"}), 356 "<foo xmlns:qux='testns2' qux:bar='baz'/>") 357 358 def testNSPrefix(self): 359 e = domish.Element((None, "foo"), 360 attribs = {("testns2", "bar"): "baz"}) 361 c = e.addElement(("testns2", "qux")) 362 c[("testns2", "bar")] = "quux" 363 364 self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'><xn0:qux xn0:bar='quux'/></foo>") 365 366 def testDefaultNSPrefix(self): 367 e = domish.Element((None, "foo"), 368 attribs = {("testns2", "bar"): "baz"}) 369 c = e.addElement(("testns2", "qux")) 370 c[("testns2", "bar")] = "quux" 371 c.addElement('foo') 372 373 self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'><xn0:qux xn0:bar='quux'><xn0:foo/></xn0:qux></foo>") 374 375 def testPrefixScope(self): 376 e = domish.Element(('testns', 'foo')) 377 378 self.assertEqual(e.toXml(prefixes={'testns': 'bar'}, 379 prefixesInScope=['bar']), 380 "<bar:foo/>") 381 382 def testLocalPrefixes(self): 383 e = domish.Element(('testns', 'foo'), localPrefixes={'bar': 'testns'}) 384 self.assertEqual(e.toXml(), "<bar:foo xmlns:bar='testns'/>") 385 386 def testLocalPrefixesWithChild(self): 387 e = domish.Element(('testns', 'foo'), localPrefixes={'bar': 'testns'}) 388 e.addElement('baz') 389 self.assertIdentical(e.baz.defaultUri, None) 390 self.assertEqual(e.toXml(), "<bar:foo xmlns:bar='testns'><baz/></bar:foo>") 391 392 def test_prefixesReuse(self): 393 """ 394 Test that prefixes passed to serialization are not modified. 395 396 This test makes sure that passing a dictionary of prefixes repeatedly 397 to C{toXml} of elements does not cause serialization errors. A 398 previous implementation changed the passed in dictionary internally, 399 causing havoc later on. 400 """ 401 prefixes = {'testns': 'foo'} 402 403 # test passing of dictionary 404 s = domish.SerializerClass(prefixes=prefixes) 405 self.assertNotIdentical(prefixes, s.prefixes) 406 407 # test proper serialization on prefixes reuse 408 e = domish.Element(('testns2', 'foo'), 409 localPrefixes={'quux': 'testns2'}) 410 self.assertEqual("<quux:foo xmlns:quux='testns2'/>", 411 e.toXml(prefixes=prefixes)) 412 e = domish.Element(('testns2', 'foo')) 413 self.assertEqual("<foo xmlns='testns2'/>", 414 e.toXml(prefixes=prefixes)) 415 416 def testRawXMLSerialization(self): 417 e = domish.Element((None, "foo")) 418 e.addRawXml("<abc123>") 419 # The testcase below should NOT generate valid XML -- that's 420 # the whole point of using the raw XML call -- it's the callers 421 # responsiblity to ensure that the data inserted is valid 422 self.assertEqual(e.toXml(), "<foo><abc123></foo>") 423 424 def testRawXMLWithUnicodeSerialization(self): 425 e = domish.Element((None, "foo")) 426 e.addRawXml(u"<degree>\u00B0</degree>") 427 self.assertEqual(e.toXml(), u"<foo><degree>\u00B0</degree></foo>") 428 429 def testUnicodeSerialization(self): 430 e = domish.Element((None, "foo")) 431 e["test"] = u"my value\u0221e" 432 e.addContent(u"A degree symbol...\u00B0") 433 self.assertEqual(e.toXml(), 434 u"<foo test='my value\u0221e'>A degree symbol...\u00B0</foo>") 435