blob: 113c7fd71d77358bcae82b6d44848741118419a7 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001# test for xml.dom.minidom
Paul Prescod7993bcc2000-07-01 14:54:16 +00002
Martin v. Löwisfd6aaa12003-01-25 22:02:52 +00003import pickle
Ezio Melotti0f12be12013-08-10 18:30:29 +03004from test.support import run_unittest, findfile
Guido van Rossumd8faa362007-04-27 19:54:29 +00005import unittest
Fred Drake17647f52000-07-03 16:37:42 +00006
Thomas Wouters0e3f5912006-08-11 14:57:12 +00007import xml.dom.minidom
Fred Drakec441f7b2002-07-19 22:16:41 +00008
Thomas Wouters0e3f5912006-08-11 14:57:12 +00009from xml.dom.minidom import parse, Node, Document, parseString
10from xml.dom.minidom import getDOMImplementation
Martin v. Löwisfd6aaa12003-01-25 22:02:52 +000011
Fred Drakec441f7b2002-07-19 22:16:41 +000012
Florent Xiclunaf15351d2010-03-13 23:24:31 +000013tstfile = findfile("test.xml", subdir="xmltestdata")
14
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000015# The tests of DocumentType importing use these helpers to construct
16# the documents to work with, since not all DOM builders actually
17# create the DocumentType nodes.
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000018def create_doc_without_doctype(doctype=None):
19 return getDOMImplementation().createDocument(None, "doc", doctype)
20
21def create_nonempty_doctype():
22 doctype = getDOMImplementation().createDocumentType("doc", None, None)
23 doctype.entities._seq = []
24 doctype.notations._seq = []
Thomas Wouters0e3f5912006-08-11 14:57:12 +000025 notation = xml.dom.minidom.Notation("my-notation", None,
26 "http://xml.python.org/notations/my")
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000027 doctype.notations._seq.append(notation)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000028 entity = xml.dom.minidom.Entity("my-entity", None,
29 "http://xml.python.org/entities/my",
30 "my-notation")
Martin v. Löwisaa5af8d2003-01-25 21:39:09 +000031 entity.version = "1.0"
32 entity.encoding = "utf-8"
33 entity.actualEncoding = "us-ascii"
34 doctype.entities._seq.append(entity)
35 return doctype
36
37def create_doc_with_doctype():
38 doctype = create_nonempty_doctype()
39 doc = create_doc_without_doctype(doctype)
40 doctype.entities.item(0).ownerDocument = doc
41 doctype.notations.item(0).ownerDocument = doc
42 return doc
43
Guido van Rossumd8faa362007-04-27 19:54:29 +000044class MinidomTest(unittest.TestCase):
Guido van Rossumd8faa362007-04-27 19:54:29 +000045 def confirm(self, test, testname = "Test"):
46 self.assertTrue(test, testname)
47
48 def checkWholeText(self, node, s):
49 t = node.wholeText
Florent Xicluna313b2ad2011-12-10 21:14:53 +010050 self.confirm(t == s, "looking for %r, found %r" % (s, t))
Guido van Rossumd8faa362007-04-27 19:54:29 +000051
52 def testParseFromFile(self):
Brett Cannonc5246922010-10-30 00:14:59 +000053 with open(tstfile) as file:
54 dom = parse(file)
55 dom.unlink()
56 self.confirm(isinstance(dom, Document))
Guido van Rossumd8faa362007-04-27 19:54:29 +000057
58 def testGetElementsByTagName(self):
59 dom = parse(tstfile)
60 self.confirm(dom.getElementsByTagName("LI") == \
61 dom.documentElement.getElementsByTagName("LI"))
62 dom.unlink()
63
64 def testInsertBefore(self):
65 dom = parseString("<doc><foo/></doc>")
66 root = dom.documentElement
67 elem = root.childNodes[0]
68 nelem = dom.createElement("element")
69 root.insertBefore(nelem, elem)
70 self.confirm(len(root.childNodes) == 2
71 and root.childNodes.length == 2
72 and root.childNodes[0] is nelem
73 and root.childNodes.item(0) is nelem
74 and root.childNodes[1] is elem
75 and root.childNodes.item(1) is elem
76 and root.firstChild is nelem
77 and root.lastChild is elem
78 and root.toxml() == "<doc><element/><foo/></doc>"
79 , "testInsertBefore -- node properly placed in tree")
80 nelem = dom.createElement("element")
81 root.insertBefore(nelem, None)
82 self.confirm(len(root.childNodes) == 3
83 and root.childNodes.length == 3
84 and root.childNodes[1] is elem
85 and root.childNodes.item(1) is elem
86 and root.childNodes[2] is nelem
87 and root.childNodes.item(2) is nelem
88 and root.lastChild is nelem
89 and nelem.previousSibling is elem
90 and root.toxml() == "<doc><element/><foo/><element/></doc>"
91 , "testInsertBefore -- node properly placed in tree")
92 nelem2 = dom.createElement("bar")
93 root.insertBefore(nelem2, nelem)
94 self.confirm(len(root.childNodes) == 4
95 and root.childNodes.length == 4
96 and root.childNodes[2] is nelem2
97 and root.childNodes.item(2) is nelem2
98 and root.childNodes[3] is nelem
99 and root.childNodes.item(3) is nelem
100 and nelem2.nextSibling is nelem
101 and nelem.previousSibling is nelem2
102 and root.toxml() ==
103 "<doc><element/><foo/><bar/><element/></doc>"
104 , "testInsertBefore -- node properly placed in tree")
105 dom.unlink()
106
107 def _create_fragment_test_nodes(self):
108 dom = parseString("<doc/>")
109 orig = dom.createTextNode("original")
110 c1 = dom.createTextNode("foo")
111 c2 = dom.createTextNode("bar")
112 c3 = dom.createTextNode("bat")
113 dom.documentElement.appendChild(orig)
114 frag = dom.createDocumentFragment()
115 frag.appendChild(c1)
116 frag.appendChild(c2)
117 frag.appendChild(c3)
118 return dom, orig, c1, c2, c3, frag
119
120 def testInsertBeforeFragment(self):
121 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
122 dom.documentElement.insertBefore(frag, None)
123 self.confirm(tuple(dom.documentElement.childNodes) ==
124 (orig, c1, c2, c3),
125 "insertBefore(<fragment>, None)")
126 frag.unlink()
127 dom.unlink()
128
129 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
130 dom.documentElement.insertBefore(frag, orig)
131 self.confirm(tuple(dom.documentElement.childNodes) ==
132 (c1, c2, c3, orig),
133 "insertBefore(<fragment>, orig)")
134 frag.unlink()
135 dom.unlink()
136
137 def testAppendChild(self):
138 dom = parse(tstfile)
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000139 dom.documentElement.appendChild(dom.createComment("Hello"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000140 self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
141 self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
142 dom.unlink()
143
144 def testAppendChildFragment(self):
145 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
146 dom.documentElement.appendChild(frag)
147 self.confirm(tuple(dom.documentElement.childNodes) ==
148 (orig, c1, c2, c3),
149 "appendChild(<fragment>)")
150 frag.unlink()
151 dom.unlink()
152
153 def testReplaceChildFragment(self):
154 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
155 dom.documentElement.replaceChild(frag, orig)
156 orig.unlink()
157 self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
158 "replaceChild(<fragment>)")
159 frag.unlink()
160 dom.unlink()
161
162 def testLegalChildren(self):
163 dom = Document()
164 elem = dom.createElement('element')
165 text = dom.createTextNode('text')
166 self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text)
167
168 dom.appendChild(elem)
169 self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text,
170 elem)
171 self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text,
172 elem)
173
174 nodemap = elem.attributes
175 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem,
176 text)
177 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS,
178 text)
179
180 elem.appendChild(text)
181 dom.unlink()
182
183 def testNamedNodeMapSetItem(self):
184 dom = Document()
185 elem = dom.createElement('element')
186 attrs = elem.attributes
187 attrs["foo"] = "bar"
188 a = attrs.item(0)
189 self.confirm(a.ownerDocument is dom,
190 "NamedNodeMap.__setitem__() sets ownerDocument")
191 self.confirm(a.ownerElement is elem,
192 "NamedNodeMap.__setitem__() sets ownerElement")
193 self.confirm(a.value == "bar",
194 "NamedNodeMap.__setitem__() sets value")
195 self.confirm(a.nodeValue == "bar",
196 "NamedNodeMap.__setitem__() sets nodeValue")
197 elem.unlink()
198 dom.unlink()
199
200 def testNonZero(self):
201 dom = parse(tstfile)
202 self.confirm(dom)# should not be zero
203 dom.appendChild(dom.createComment("foo"))
204 self.confirm(not dom.childNodes[-1].childNodes)
205 dom.unlink()
206
207 def testUnlink(self):
208 dom = parse(tstfile)
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000209 self.assertTrue(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000210 dom.unlink()
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000211 self.assertFalse(dom.childNodes)
212
213 def testContext(self):
214 with parse(tstfile) as dom:
215 self.assertTrue(dom.childNodes)
216 self.assertFalse(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000217
218 def testElement(self):
219 dom = Document()
220 dom.appendChild(dom.createElement("abc"))
221 self.confirm(dom.documentElement)
222 dom.unlink()
223
224 def testAAA(self):
225 dom = parseString("<abc/>")
226 el = dom.documentElement
227 el.setAttribute("spam", "jam2")
228 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA")
229 a = el.getAttributeNode("spam")
230 self.confirm(a.ownerDocument is dom,
231 "setAttribute() sets ownerDocument")
232 self.confirm(a.ownerElement is dom.documentElement,
233 "setAttribute() sets ownerElement")
234 dom.unlink()
235
236 def testAAB(self):
237 dom = parseString("<abc/>")
238 el = dom.documentElement
239 el.setAttribute("spam", "jam")
240 el.setAttribute("spam", "jam2")
241 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB")
242 dom.unlink()
243
244 def testAddAttr(self):
245 dom = Document()
246 child = dom.appendChild(dom.createElement("abc"))
247
248 child.setAttribute("def", "ghi")
249 self.confirm(child.getAttribute("def") == "ghi")
250 self.confirm(child.attributes["def"].value == "ghi")
251
252 child.setAttribute("jkl", "mno")
253 self.confirm(child.getAttribute("jkl") == "mno")
254 self.confirm(child.attributes["jkl"].value == "mno")
255
256 self.confirm(len(child.attributes) == 2)
257
258 child.setAttribute("def", "newval")
259 self.confirm(child.getAttribute("def") == "newval")
260 self.confirm(child.attributes["def"].value == "newval")
261
262 self.confirm(len(child.attributes) == 2)
263 dom.unlink()
264
265 def testDeleteAttr(self):
266 dom = Document()
267 child = dom.appendChild(dom.createElement("abc"))
268
269 self.confirm(len(child.attributes) == 0)
270 child.setAttribute("def", "ghi")
271 self.confirm(len(child.attributes) == 1)
272 del child.attributes["def"]
273 self.confirm(len(child.attributes) == 0)
274 dom.unlink()
275
276 def testRemoveAttr(self):
277 dom = Document()
278 child = dom.appendChild(dom.createElement("abc"))
279
280 child.setAttribute("def", "ghi")
281 self.confirm(len(child.attributes) == 1)
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200282 self.assertRaises(xml.dom.NotFoundErr, child.removeAttribute, "foo")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000283 child.removeAttribute("def")
284 self.confirm(len(child.attributes) == 0)
285 dom.unlink()
286
287 def testRemoveAttrNS(self):
288 dom = Document()
289 child = dom.appendChild(
290 dom.createElementNS("http://www.python.org", "python:abc"))
291 child.setAttributeNS("http://www.w3.org", "xmlns:python",
292 "http://www.python.org")
293 child.setAttributeNS("http://www.python.org", "python:abcattr", "foo")
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200294 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNS,
295 "foo", "http://www.python.org")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000296 self.confirm(len(child.attributes) == 2)
297 child.removeAttributeNS("http://www.python.org", "abcattr")
298 self.confirm(len(child.attributes) == 1)
299 dom.unlink()
300
301 def testRemoveAttributeNode(self):
302 dom = Document()
303 child = dom.appendChild(dom.createElement("foo"))
304 child.setAttribute("spam", "jam")
305 self.confirm(len(child.attributes) == 1)
306 node = child.getAttributeNode("spam")
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200307 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNode,
308 None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000309 child.removeAttributeNode(node)
310 self.confirm(len(child.attributes) == 0
311 and child.getAttributeNode("spam") is None)
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200312 dom2 = Document()
Ezio Melotti0f12be12013-08-10 18:30:29 +0300313 child2 = dom2.appendChild(dom2.createElement("foo"))
314 node2 = child2.getAttributeNode("spam")
315 self.assertRaises(xml.dom.NotFoundErr, child2.removeAttributeNode,
316 node2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000317 dom.unlink()
318
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200319 def testHasAttribute(self):
320 dom = Document()
321 child = dom.appendChild(dom.createElement("foo"))
322 child.setAttribute("spam", "jam")
323 self.confirm(child.hasAttribute("spam"))
324
Guido van Rossumd8faa362007-04-27 19:54:29 +0000325 def testChangeAttr(self):
326 dom = parseString("<abc/>")
327 el = dom.documentElement
328 el.setAttribute("spam", "jam")
329 self.confirm(len(el.attributes) == 1)
330 el.setAttribute("spam", "bam")
331 # Set this attribute to be an ID and make sure that doesn't change
332 # when changing the value:
333 el.setIdAttribute("spam")
334 self.confirm(len(el.attributes) == 1
335 and el.attributes["spam"].value == "bam"
336 and el.attributes["spam"].nodeValue == "bam"
337 and el.getAttribute("spam") == "bam"
338 and el.getAttributeNode("spam").isId)
339 el.attributes["spam"] = "ham"
340 self.confirm(len(el.attributes) == 1
341 and el.attributes["spam"].value == "ham"
342 and el.attributes["spam"].nodeValue == "ham"
343 and el.getAttribute("spam") == "ham"
344 and el.attributes["spam"].isId)
345 el.setAttribute("spam2", "bam")
346 self.confirm(len(el.attributes) == 2
347 and el.attributes["spam"].value == "ham"
348 and el.attributes["spam"].nodeValue == "ham"
349 and el.getAttribute("spam") == "ham"
350 and el.attributes["spam2"].value == "bam"
351 and el.attributes["spam2"].nodeValue == "bam"
352 and el.getAttribute("spam2") == "bam")
353 el.attributes["spam2"] = "bam2"
354 self.confirm(len(el.attributes) == 2
355 and el.attributes["spam"].value == "ham"
356 and el.attributes["spam"].nodeValue == "ham"
357 and el.getAttribute("spam") == "ham"
358 and el.attributes["spam2"].value == "bam2"
359 and el.attributes["spam2"].nodeValue == "bam2"
360 and el.getAttribute("spam2") == "bam2")
361 dom.unlink()
362
Martin v. Löwis67245a62012-03-05 07:01:49 +0100363 def testGetAttribute(self):
364 dom = Document()
365 child = dom.appendChild(
366 dom.createElementNS("http://www.python.org", "python:abc"))
367 self.assertEqual(child.getAttribute('missing'), '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000368
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200369 def testGetAttributeNS(self):
370 dom = Document()
371 child = dom.appendChild(
372 dom.createElementNS("http://www.python.org", "python:abc"))
373 child.setAttributeNS("http://www.w3.org", "xmlns:python",
374 "http://www.python.org")
375 self.assertEqual(child.getAttributeNS("http://www.w3.org", "python"),
376 'http://www.python.org')
377 self.assertEqual(child.getAttributeNS("http://www.w3.org", "other"),
378 '')
Martin v. Löwis67245a62012-03-05 07:01:49 +0100379 child2 = child.appendChild(dom.createElement('abc'))
380 self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"),
381 '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000382
Guido van Rossumd8faa362007-04-27 19:54:29 +0000383 def testGetElementsByTagNameNS(self):
384 d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'>
385 <minidom:myelem/>
386 </foo>"""
387 dom = parseString(d)
388 elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom",
389 "myelem")
390 self.confirm(len(elems) == 1
391 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom"
392 and elems[0].localName == "myelem"
393 and elems[0].prefix == "minidom"
394 and elems[0].tagName == "minidom:myelem"
395 and elems[0].nodeName == "minidom:myelem")
396 dom.unlink()
397
398 def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri,
399 lname):
400 nodelist = doc.getElementsByTagNameNS(nsuri, lname)
401 self.confirm(len(nodelist) == 0)
402
403 def testGetEmptyNodeListFromElementsByTagNameNS(self):
404 doc = parseString('<doc/>')
405 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
406 doc, 'http://xml.python.org/namespaces/a', 'localname')
407 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
408 doc, '*', 'splat')
409 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
410 doc, 'http://xml.python.org/namespaces/a', '*')
411
412 doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>')
413 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
414 doc, "http://xml.python.org/splat", "not-there")
415 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
416 doc, "*", "not-there")
417 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
418 doc, "http://somewhere.else.net/not-there", "e")
419
420 def testElementReprAndStr(self):
421 dom = Document()
422 el = dom.appendChild(dom.createElement("abc"))
423 string1 = repr(el)
424 string2 = str(el)
425 self.confirm(string1 == string2)
426 dom.unlink()
427
428 def testElementReprAndStrUnicode(self):
429 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000430 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000431 string1 = repr(el)
432 string2 = str(el)
433 self.confirm(string1 == string2)
434 dom.unlink()
435
436 def testElementReprAndStrUnicodeNS(self):
437 dom = Document()
438 el = dom.appendChild(
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000439 dom.createElementNS("http://www.slashdot.org", "slash:abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000440 string1 = repr(el)
441 string2 = str(el)
442 self.confirm(string1 == string2)
Ezio Melotti7fb4da72010-03-18 12:29:13 +0000443 self.confirm("slash:abc" in string1)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000444 dom.unlink()
445
446 def testAttributeRepr(self):
447 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000448 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000449 node = el.setAttribute("abc", "def")
450 self.confirm(str(node) == repr(node))
451 dom.unlink()
452
Guido van Rossumd8faa362007-04-27 19:54:29 +0000453 def testWriteXML(self):
454 str = '<?xml version="1.0" ?><a b="c"/>'
455 dom = parseString(str)
456 domstr = dom.toxml()
457 dom.unlink()
458 self.confirm(str == domstr)
459
460 def testAltNewline(self):
461 str = '<?xml version="1.0" ?>\n<a b="c"/>\n'
462 dom = parseString(str)
463 domstr = dom.toprettyxml(newl="\r\n")
464 dom.unlink()
465 self.confirm(domstr == str.replace("\n", "\r\n"))
466
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200467 def test_toprettyxml_with_text_nodes(self):
468 # see issue #4147, text nodes are not indented
469 decl = '<?xml version="1.0" ?>\n'
470 self.assertEqual(parseString('<B>A</B>').toprettyxml(),
471 decl + '<B>A</B>\n')
472 self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
473 decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
474 self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
475 decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
476 self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
477 decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
478 self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
479 decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
480
481 def test_toprettyxml_with_adjacent_text_nodes(self):
482 # see issue #4147, adjacent text nodes are indented normally
483 dom = Document()
484 elem = dom.createElement('elem')
485 elem.appendChild(dom.createTextNode('TEXT'))
486 elem.appendChild(dom.createTextNode('TEXT'))
487 dom.appendChild(elem)
488 decl = '<?xml version="1.0" ?>\n'
489 self.assertEqual(dom.toprettyxml(),
490 decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
491
Éric Araujo63ba97b2011-10-05 01:29:22 +0200492 def test_toprettyxml_preserves_content_of_text_node(self):
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200493 # see issue #4147
494 for str in ('<B>A</B>', '<A><B>C</B></A>'):
495 dom = parseString(str)
496 dom2 = parseString(dom.toprettyxml())
497 self.assertEqual(
498 dom.getElementsByTagName('B')[0].childNodes[0].toxml(),
499 dom2.getElementsByTagName('B')[0].childNodes[0].toxml())
R David Murray791744b2011-10-01 16:19:51 -0400500
Guido van Rossumd8faa362007-04-27 19:54:29 +0000501 def testProcessingInstruction(self):
502 dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
503 pi = dom.documentElement.firstChild
504 self.confirm(pi.target == "mypi"
505 and pi.data == "data \t\n "
506 and pi.nodeName == "mypi"
507 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
508 and pi.attributes is None
509 and not pi.hasChildNodes()
510 and len(pi.childNodes) == 0
511 and pi.firstChild is None
512 and pi.lastChild is None
513 and pi.localName is None
514 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
515
Guido van Rossumd8faa362007-04-27 19:54:29 +0000516 def testTooManyDocumentElements(self):
517 doc = parseString("<doc/>")
518 elem = doc.createElement("extra")
519 # Should raise an exception when adding an extra document element.
520 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
521 elem.unlink()
522 doc.unlink()
523
Guido van Rossumd8faa362007-04-27 19:54:29 +0000524 def testRemoveNamedItem(self):
525 doc = parseString("<doc a=''/>")
526 e = doc.documentElement
527 attrs = e.attributes
528 a1 = e.getAttributeNode("a")
529 a2 = attrs.removeNamedItem("a")
530 self.confirm(a1.isSameNode(a2))
531 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
532
533 def testRemoveNamedItemNS(self):
534 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
535 e = doc.documentElement
536 attrs = e.attributes
537 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
538 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
539 self.confirm(a1.isSameNode(a2))
540 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
541 "http://xml.python.org/", "b")
542
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200543 def testHasChildNodes(self):
544 dom = parseString("<doc><foo/></doc>")
545 doc = dom.documentElement
Ezio Melotti0f12be12013-08-10 18:30:29 +0300546 self.assertTrue(doc.hasChildNodes())
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200547 dom2 = parseString("<doc/>")
548 doc2 = dom2.documentElement
549 self.assertFalse(doc2.hasChildNodes())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000550
551 def _testCloneElementCopiesAttributes(self, e1, e2, test):
552 attrs1 = e1.attributes
553 attrs2 = e2.attributes
554 keys1 = list(attrs1.keys())
555 keys2 = list(attrs2.keys())
556 keys1.sort()
557 keys2.sort()
558 self.confirm(keys1 == keys2, "clone of element has same attribute keys")
559 for i in range(len(keys1)):
560 a1 = attrs1.item(i)
561 a2 = attrs2.item(i)
562 self.confirm(a1 is not a2
563 and a1.value == a2.value
564 and a1.nodeValue == a2.nodeValue
565 and a1.namespaceURI == a2.namespaceURI
566 and a1.localName == a2.localName
567 , "clone of attribute node has proper attribute values")
568 self.confirm(a2.ownerElement is e2,
569 "clone of attribute node correctly owned")
570
571 def _setupCloneElement(self, deep):
572 dom = parseString("<doc attr='value'><foo/></doc>")
573 root = dom.documentElement
574 clone = root.cloneNode(deep)
575 self._testCloneElementCopiesAttributes(
576 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
577 # mutilate the original so shared data is detected
578 root.tagName = root.nodeName = "MODIFIED"
579 root.setAttribute("attr", "NEW VALUE")
580 root.setAttribute("added", "VALUE")
581 return dom, clone
582
583 def testCloneElementShallow(self):
584 dom, clone = self._setupCloneElement(0)
585 self.confirm(len(clone.childNodes) == 0
586 and clone.childNodes.length == 0
587 and clone.parentNode is None
588 and clone.toxml() == '<doc attr="value"/>'
589 , "testCloneElementShallow")
590 dom.unlink()
591
592 def testCloneElementDeep(self):
593 dom, clone = self._setupCloneElement(1)
594 self.confirm(len(clone.childNodes) == 1
595 and clone.childNodes.length == 1
596 and clone.parentNode is None
597 and clone.toxml() == '<doc attr="value"><foo/></doc>'
598 , "testCloneElementDeep")
599 dom.unlink()
600
601 def testCloneDocumentShallow(self):
602 doc = parseString("<?xml version='1.0'?>\n"
603 "<!-- comment -->"
604 "<!DOCTYPE doc [\n"
605 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
606 "]>\n"
607 "<doc attr='value'/>")
608 doc2 = doc.cloneNode(0)
609 self.confirm(doc2 is None,
610 "testCloneDocumentShallow:"
611 " shallow cloning of documents makes no sense!")
612
613 def testCloneDocumentDeep(self):
614 doc = parseString("<?xml version='1.0'?>\n"
615 "<!-- comment -->"
616 "<!DOCTYPE doc [\n"
617 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
618 "]>\n"
619 "<doc attr='value'/>")
620 doc2 = doc.cloneNode(1)
621 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
622 "testCloneDocumentDeep: document objects not distinct")
623 self.confirm(len(doc.childNodes) == len(doc2.childNodes),
624 "testCloneDocumentDeep: wrong number of Document children")
625 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
626 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
627 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
628 "testCloneDocumentDeep: documentElement owner is not new document")
629 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
630 "testCloneDocumentDeep: documentElement should not be shared")
631 if doc.doctype is not None:
632 # check the doctype iff the original DOM maintained it
633 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
634 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
635 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
636 self.confirm(not doc.doctype.isSameNode(doc2.doctype))
637
638 def testCloneDocumentTypeDeepOk(self):
639 doctype = create_nonempty_doctype()
640 clone = doctype.cloneNode(1)
641 self.confirm(clone is not None
642 and clone.nodeName == doctype.nodeName
643 and clone.name == doctype.name
644 and clone.publicId == doctype.publicId
645 and clone.systemId == doctype.systemId
646 and len(clone.entities) == len(doctype.entities)
647 and clone.entities.item(len(clone.entities)) is None
648 and len(clone.notations) == len(doctype.notations)
649 and clone.notations.item(len(clone.notations)) is None
650 and len(clone.childNodes) == 0)
651 for i in range(len(doctype.entities)):
652 se = doctype.entities.item(i)
653 ce = clone.entities.item(i)
654 self.confirm((not se.isSameNode(ce))
655 and (not ce.isSameNode(se))
656 and ce.nodeName == se.nodeName
657 and ce.notationName == se.notationName
658 and ce.publicId == se.publicId
659 and ce.systemId == se.systemId
660 and ce.encoding == se.encoding
661 and ce.actualEncoding == se.actualEncoding
662 and ce.version == se.version)
663 for i in range(len(doctype.notations)):
664 sn = doctype.notations.item(i)
665 cn = clone.notations.item(i)
666 self.confirm((not sn.isSameNode(cn))
667 and (not cn.isSameNode(sn))
668 and cn.nodeName == sn.nodeName
669 and cn.publicId == sn.publicId
670 and cn.systemId == sn.systemId)
671
672 def testCloneDocumentTypeDeepNotOk(self):
673 doc = create_doc_with_doctype()
674 clone = doc.doctype.cloneNode(1)
675 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
676
677 def testCloneDocumentTypeShallowOk(self):
678 doctype = create_nonempty_doctype()
679 clone = doctype.cloneNode(0)
680 self.confirm(clone is not None
681 and clone.nodeName == doctype.nodeName
682 and clone.name == doctype.name
683 and clone.publicId == doctype.publicId
684 and clone.systemId == doctype.systemId
685 and len(clone.entities) == 0
686 and clone.entities.item(0) is None
687 and len(clone.notations) == 0
688 and clone.notations.item(0) is None
689 and len(clone.childNodes) == 0)
690
691 def testCloneDocumentTypeShallowNotOk(self):
692 doc = create_doc_with_doctype()
693 clone = doc.doctype.cloneNode(0)
694 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
695
696 def check_import_document(self, deep, testName):
697 doc1 = parseString("<doc/>")
698 doc2 = parseString("<doc/>")
699 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
700
701 def testImportDocumentShallow(self):
702 self.check_import_document(0, "testImportDocumentShallow")
703
704 def testImportDocumentDeep(self):
705 self.check_import_document(1, "testImportDocumentDeep")
706
707 def testImportDocumentTypeShallow(self):
708 src = create_doc_with_doctype()
709 target = create_doc_without_doctype()
710 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
711 src.doctype, 0)
712
713 def testImportDocumentTypeDeep(self):
714 src = create_doc_with_doctype()
715 target = create_doc_without_doctype()
716 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
717 src.doctype, 1)
718
719 # Testing attribute clones uses a helper, and should always be deep,
720 # even if the argument to cloneNode is false.
721 def check_clone_attribute(self, deep, testName):
722 doc = parseString("<doc attr='value'/>")
723 attr = doc.documentElement.getAttributeNode("attr")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000724 self.assertNotEqual(attr, None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000725 clone = attr.cloneNode(deep)
726 self.confirm(not clone.isSameNode(attr))
727 self.confirm(not attr.isSameNode(clone))
728 self.confirm(clone.ownerElement is None,
729 testName + ": ownerElement should be None")
730 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
731 testName + ": ownerDocument does not match")
732 self.confirm(clone.specified,
733 testName + ": cloned attribute must have specified == True")
734
735 def testCloneAttributeShallow(self):
736 self.check_clone_attribute(0, "testCloneAttributeShallow")
737
738 def testCloneAttributeDeep(self):
739 self.check_clone_attribute(1, "testCloneAttributeDeep")
740
741 def check_clone_pi(self, deep, testName):
742 doc = parseString("<?target data?><doc/>")
743 pi = doc.firstChild
Ezio Melottib3aedd42010-11-20 19:04:17 +0000744 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000745 clone = pi.cloneNode(deep)
746 self.confirm(clone.target == pi.target
747 and clone.data == pi.data)
748
749 def testClonePIShallow(self):
750 self.check_clone_pi(0, "testClonePIShallow")
751
752 def testClonePIDeep(self):
753 self.check_clone_pi(1, "testClonePIDeep")
754
755 def testNormalize(self):
756 doc = parseString("<doc/>")
757 root = doc.documentElement
758 root.appendChild(doc.createTextNode("first"))
759 root.appendChild(doc.createTextNode("second"))
760 self.confirm(len(root.childNodes) == 2
761 and root.childNodes.length == 2,
762 "testNormalize -- preparation")
763 doc.normalize()
764 self.confirm(len(root.childNodes) == 1
765 and root.childNodes.length == 1
766 and root.firstChild is root.lastChild
767 and root.firstChild.data == "firstsecond"
768 , "testNormalize -- result")
769 doc.unlink()
770
771 doc = parseString("<doc/>")
772 root = doc.documentElement
773 root.appendChild(doc.createTextNode(""))
774 doc.normalize()
775 self.confirm(len(root.childNodes) == 0
776 and root.childNodes.length == 0,
777 "testNormalize -- single empty node removed")
778 doc.unlink()
779
R. David Murraydc6da8a2009-04-09 22:16:43 +0000780 def testNormalizeCombineAndNextSibling(self):
781 doc = parseString("<doc/>")
782 root = doc.documentElement
783 root.appendChild(doc.createTextNode("first"))
784 root.appendChild(doc.createTextNode("second"))
785 root.appendChild(doc.createElement("i"))
786 self.confirm(len(root.childNodes) == 3
787 and root.childNodes.length == 3,
788 "testNormalizeCombineAndNextSibling -- preparation")
789 doc.normalize()
790 self.confirm(len(root.childNodes) == 2
791 and root.childNodes.length == 2
792 and root.firstChild.data == "firstsecond"
793 and root.firstChild is not root.lastChild
794 and root.firstChild.nextSibling is root.lastChild
795 and root.firstChild.previousSibling is None
796 and root.lastChild.previousSibling is root.firstChild
797 and root.lastChild.nextSibling is None
798 , "testNormalizeCombinedAndNextSibling -- result")
799 doc.unlink()
800
801 def testNormalizeDeleteWithPrevSibling(self):
802 doc = parseString("<doc/>")
803 root = doc.documentElement
804 root.appendChild(doc.createTextNode("first"))
805 root.appendChild(doc.createTextNode(""))
806 self.confirm(len(root.childNodes) == 2
807 and root.childNodes.length == 2,
808 "testNormalizeDeleteWithPrevSibling -- preparation")
809 doc.normalize()
810 self.confirm(len(root.childNodes) == 1
811 and root.childNodes.length == 1
812 and root.firstChild.data == "first"
813 and root.firstChild is root.lastChild
814 and root.firstChild.nextSibling is None
815 and root.firstChild.previousSibling is None
816 , "testNormalizeDeleteWithPrevSibling -- result")
817 doc.unlink()
818
819 def testNormalizeDeleteWithNextSibling(self):
820 doc = parseString("<doc/>")
821 root = doc.documentElement
822 root.appendChild(doc.createTextNode(""))
823 root.appendChild(doc.createTextNode("second"))
824 self.confirm(len(root.childNodes) == 2
825 and root.childNodes.length == 2,
826 "testNormalizeDeleteWithNextSibling -- preparation")
827 doc.normalize()
828 self.confirm(len(root.childNodes) == 1
829 and root.childNodes.length == 1
830 and root.firstChild.data == "second"
831 and root.firstChild is root.lastChild
832 and root.firstChild.nextSibling is None
833 and root.firstChild.previousSibling is None
834 , "testNormalizeDeleteWithNextSibling -- result")
835 doc.unlink()
836
837 def testNormalizeDeleteWithTwoNonTextSiblings(self):
838 doc = parseString("<doc/>")
839 root = doc.documentElement
840 root.appendChild(doc.createElement("i"))
841 root.appendChild(doc.createTextNode(""))
842 root.appendChild(doc.createElement("i"))
843 self.confirm(len(root.childNodes) == 3
844 and root.childNodes.length == 3,
845 "testNormalizeDeleteWithTwoSiblings -- preparation")
846 doc.normalize()
847 self.confirm(len(root.childNodes) == 2
848 and root.childNodes.length == 2
849 and root.firstChild is not root.lastChild
850 and root.firstChild.nextSibling is root.lastChild
851 and root.firstChild.previousSibling is None
852 and root.lastChild.previousSibling is root.firstChild
853 and root.lastChild.nextSibling is None
854 , "testNormalizeDeleteWithTwoSiblings -- result")
855 doc.unlink()
856
857 def testNormalizeDeleteAndCombine(self):
858 doc = parseString("<doc/>")
859 root = doc.documentElement
860 root.appendChild(doc.createTextNode(""))
861 root.appendChild(doc.createTextNode("second"))
862 root.appendChild(doc.createTextNode(""))
863 root.appendChild(doc.createTextNode("fourth"))
864 root.appendChild(doc.createTextNode(""))
865 self.confirm(len(root.childNodes) == 5
866 and root.childNodes.length == 5,
867 "testNormalizeDeleteAndCombine -- preparation")
868 doc.normalize()
869 self.confirm(len(root.childNodes) == 1
870 and root.childNodes.length == 1
871 and root.firstChild is root.lastChild
872 and root.firstChild.data == "secondfourth"
873 and root.firstChild.previousSibling is None
874 and root.firstChild.nextSibling is None
875 , "testNormalizeDeleteAndCombine -- result")
876 doc.unlink()
877
878 def testNormalizeRecursion(self):
879 doc = parseString("<doc>"
880 "<o>"
881 "<i/>"
882 "t"
883 #
884 #x
885 "</o>"
886 "<o>"
887 "<o>"
888 "t2"
889 #x2
890 "</o>"
891 "t3"
892 #x3
893 "</o>"
894 #
895 "</doc>")
896 root = doc.documentElement
897 root.childNodes[0].appendChild(doc.createTextNode(""))
898 root.childNodes[0].appendChild(doc.createTextNode("x"))
899 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
900 root.childNodes[1].appendChild(doc.createTextNode("x3"))
901 root.appendChild(doc.createTextNode(""))
902 self.confirm(len(root.childNodes) == 3
903 and root.childNodes.length == 3
904 and len(root.childNodes[0].childNodes) == 4
905 and root.childNodes[0].childNodes.length == 4
906 and len(root.childNodes[1].childNodes) == 3
907 and root.childNodes[1].childNodes.length == 3
908 and len(root.childNodes[1].childNodes[0].childNodes) == 2
909 and root.childNodes[1].childNodes[0].childNodes.length == 2
910 , "testNormalize2 -- preparation")
911 doc.normalize()
912 self.confirm(len(root.childNodes) == 2
913 and root.childNodes.length == 2
914 and len(root.childNodes[0].childNodes) == 2
915 and root.childNodes[0].childNodes.length == 2
916 and len(root.childNodes[1].childNodes) == 2
917 and root.childNodes[1].childNodes.length == 2
918 and len(root.childNodes[1].childNodes[0].childNodes) == 1
919 and root.childNodes[1].childNodes[0].childNodes.length == 1
920 , "testNormalize2 -- childNodes lengths")
921 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
922 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
923 and root.childNodes[1].childNodes[1].data == "t3x3"
924 , "testNormalize2 -- joined text fields")
925 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
926 and root.childNodes[0].childNodes[1].previousSibling
927 is root.childNodes[0].childNodes[0]
928 and root.childNodes[0].childNodes[0].previousSibling is None
929 and root.childNodes[0].childNodes[0].nextSibling
930 is root.childNodes[0].childNodes[1]
931 and root.childNodes[1].childNodes[1].nextSibling is None
932 and root.childNodes[1].childNodes[1].previousSibling
933 is root.childNodes[1].childNodes[0]
934 and root.childNodes[1].childNodes[0].previousSibling is None
935 and root.childNodes[1].childNodes[0].nextSibling
936 is root.childNodes[1].childNodes[1]
937 , "testNormalize2 -- sibling pointers")
938 doc.unlink()
939
940
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000941 def testBug0777884(self):
942 doc = parseString("<o>text</o>")
943 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +0000944 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +0000945 # Should run quietly, doing nothing.
946 text.normalize()
947 doc.unlink()
948
Christian Heimes05e8be12008-02-23 18:30:17 +0000949 def testBug1433694(self):
950 doc = parseString("<o><i/>t</o>")
951 node = doc.documentElement
952 node.childNodes[1].nodeValue = ""
953 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +0000954 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +0000955 "Final child's .nextSibling should be None")
956
Guido van Rossumd8faa362007-04-27 19:54:29 +0000957 def testSiblings(self):
958 doc = parseString("<doc><?pi?>text?<elm/></doc>")
959 root = doc.documentElement
960 (pi, text, elm) = root.childNodes
961
962 self.confirm(pi.nextSibling is text and
963 pi.previousSibling is None and
964 text.nextSibling is elm and
965 text.previousSibling is pi and
966 elm.nextSibling is None and
967 elm.previousSibling is text, "testSiblings")
968
969 doc.unlink()
970
971 def testParents(self):
972 doc = parseString(
973 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
974 root = doc.documentElement
975 elm1 = root.childNodes[0]
976 (elm2a, elm2b) = elm1.childNodes
977 elm3 = elm2b.childNodes[0]
978
979 self.confirm(root.parentNode is doc and
980 elm1.parentNode is root and
981 elm2a.parentNode is elm1 and
982 elm2b.parentNode is elm1 and
983 elm3.parentNode is elm2b, "testParents")
984 doc.unlink()
985
986 def testNodeListItem(self):
987 doc = parseString("<doc><e/><e/></doc>")
988 children = doc.childNodes
989 docelem = children[0]
990 self.confirm(children[0] is children.item(0)
991 and children.item(1) is None
992 and docelem.childNodes.item(0) is docelem.childNodes[0]
993 and docelem.childNodes.item(1) is docelem.childNodes[1]
994 and docelem.childNodes.item(0).childNodes.item(0) is None,
995 "test NodeList.item()")
996 doc.unlink()
997
Guido van Rossumd8faa362007-04-27 19:54:29 +0000998 def testEncodings(self):
999 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001000 self.assertEqual(doc.toxml(),
1001 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1002 self.assertEqual(doc.toxml('utf-8'),
1003 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1004 self.assertEqual(doc.toxml('iso-8859-15'),
1005 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Eli Bendersky8a805022012-07-13 09:52:39 +03001006 self.assertEqual(doc.toxml('us-ascii'),
1007 b'<?xml version="1.0" encoding="us-ascii"?><foo>&#8364;</foo>')
1008 self.assertEqual(doc.toxml('utf-16'),
1009 '<?xml version="1.0" encoding="utf-16"?>'
1010 '<foo>\u20ac</foo>'.encode('utf-16'))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001011
Andrew Svetlov737fb892012-12-18 21:14:22 +02001012 # Verify that character decoding errors raise exceptions instead
Guido van Rossumd8faa362007-04-27 19:54:29 +00001013 # of crashing
1014 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001015 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001016
1017 doc.unlink()
1018
1019 class UserDataHandler:
1020 called = 0
1021 def handle(self, operation, key, data, src, dst):
1022 dst.setUserData(key, data + 1, self)
1023 src.setUserData(key, None, None)
1024 self.called = 1
1025
1026 def testUserData(self):
1027 dom = Document()
1028 n = dom.createElement('e')
1029 self.confirm(n.getUserData("foo") is None)
1030 n.setUserData("foo", None, None)
1031 self.confirm(n.getUserData("foo") is None)
1032 n.setUserData("foo", 12, 12)
1033 n.setUserData("bar", 13, 13)
1034 self.confirm(n.getUserData("foo") == 12)
1035 self.confirm(n.getUserData("bar") == 13)
1036 n.setUserData("foo", None, None)
1037 self.confirm(n.getUserData("foo") is None)
1038 self.confirm(n.getUserData("bar") == 13)
1039
1040 handler = self.UserDataHandler()
1041 n.setUserData("bar", 12, handler)
1042 c = n.cloneNode(1)
1043 self.confirm(handler.called
1044 and n.getUserData("bar") is None
1045 and c.getUserData("bar") == 13)
1046 n.unlink()
1047 c.unlink()
1048 dom.unlink()
1049
1050 def checkRenameNodeSharedConstraints(self, doc, node):
1051 # Make sure illegal NS usage is detected:
1052 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1053 "http://xml.python.org/ns", "xmlns:foo")
1054 doc2 = parseString("<doc/>")
1055 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1056 xml.dom.EMPTY_NAMESPACE, "foo")
1057
1058 def testRenameAttribute(self):
1059 doc = parseString("<doc a='v'/>")
1060 elem = doc.documentElement
1061 attrmap = elem.attributes
1062 attr = elem.attributes['a']
1063
1064 # Simple renaming
1065 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1066 self.confirm(attr.name == "b"
1067 and attr.nodeName == "b"
1068 and attr.localName is None
1069 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1070 and attr.prefix is None
1071 and attr.value == "v"
1072 and elem.getAttributeNode("a") is None
1073 and elem.getAttributeNode("b").isSameNode(attr)
1074 and attrmap["b"].isSameNode(attr)
1075 and attr.ownerDocument.isSameNode(doc)
1076 and attr.ownerElement.isSameNode(elem))
1077
1078 # Rename to have a namespace, no prefix
1079 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1080 self.confirm(attr.name == "c"
1081 and attr.nodeName == "c"
1082 and attr.localName == "c"
1083 and attr.namespaceURI == "http://xml.python.org/ns"
1084 and attr.prefix is None
1085 and attr.value == "v"
1086 and elem.getAttributeNode("a") is None
1087 and elem.getAttributeNode("b") is None
1088 and elem.getAttributeNode("c").isSameNode(attr)
1089 and elem.getAttributeNodeNS(
1090 "http://xml.python.org/ns", "c").isSameNode(attr)
1091 and attrmap["c"].isSameNode(attr)
1092 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1093
1094 # Rename to have a namespace, with prefix
1095 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1096 self.confirm(attr.name == "p:d"
1097 and attr.nodeName == "p:d"
1098 and attr.localName == "d"
1099 and attr.namespaceURI == "http://xml.python.org/ns2"
1100 and attr.prefix == "p"
1101 and attr.value == "v"
1102 and elem.getAttributeNode("a") is None
1103 and elem.getAttributeNode("b") is None
1104 and elem.getAttributeNode("c") is None
1105 and elem.getAttributeNodeNS(
1106 "http://xml.python.org/ns", "c") is None
1107 and elem.getAttributeNode("p:d").isSameNode(attr)
1108 and elem.getAttributeNodeNS(
1109 "http://xml.python.org/ns2", "d").isSameNode(attr)
1110 and attrmap["p:d"].isSameNode(attr)
1111 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1112
1113 # Rename back to a simple non-NS node
1114 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1115 self.confirm(attr.name == "e"
1116 and attr.nodeName == "e"
1117 and attr.localName is None
1118 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1119 and attr.prefix is None
1120 and attr.value == "v"
1121 and elem.getAttributeNode("a") is None
1122 and elem.getAttributeNode("b") is None
1123 and elem.getAttributeNode("c") is None
1124 and elem.getAttributeNode("p:d") is None
1125 and elem.getAttributeNodeNS(
1126 "http://xml.python.org/ns", "c") is None
1127 and elem.getAttributeNode("e").isSameNode(attr)
1128 and attrmap["e"].isSameNode(attr))
1129
1130 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1131 "http://xml.python.org/ns", "xmlns")
1132 self.checkRenameNodeSharedConstraints(doc, attr)
1133 doc.unlink()
1134
1135 def testRenameElement(self):
1136 doc = parseString("<doc/>")
1137 elem = doc.documentElement
1138
1139 # Simple renaming
1140 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1141 self.confirm(elem.tagName == "a"
1142 and elem.nodeName == "a"
1143 and elem.localName is None
1144 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1145 and elem.prefix is None
1146 and elem.ownerDocument.isSameNode(doc))
1147
1148 # Rename to have a namespace, no prefix
1149 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1150 self.confirm(elem.tagName == "b"
1151 and elem.nodeName == "b"
1152 and elem.localName == "b"
1153 and elem.namespaceURI == "http://xml.python.org/ns"
1154 and elem.prefix is None
1155 and elem.ownerDocument.isSameNode(doc))
1156
1157 # Rename to have a namespace, with prefix
1158 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1159 self.confirm(elem.tagName == "p:c"
1160 and elem.nodeName == "p:c"
1161 and elem.localName == "c"
1162 and elem.namespaceURI == "http://xml.python.org/ns2"
1163 and elem.prefix == "p"
1164 and elem.ownerDocument.isSameNode(doc))
1165
1166 # Rename back to a simple non-NS node
1167 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1168 self.confirm(elem.tagName == "d"
1169 and elem.nodeName == "d"
1170 and elem.localName is None
1171 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1172 and elem.prefix is None
1173 and elem.ownerDocument.isSameNode(doc))
1174
1175 self.checkRenameNodeSharedConstraints(doc, elem)
1176 doc.unlink()
1177
1178 def testRenameOther(self):
1179 # We have to create a comment node explicitly since not all DOM
1180 # builders used with minidom add comments to the DOM.
1181 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1182 xml.dom.EMPTY_NAMESPACE, "e", None)
1183 node = doc.createComment("comment")
1184 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1185 xml.dom.EMPTY_NAMESPACE, "foo")
1186 doc.unlink()
1187
1188 def testWholeText(self):
1189 doc = parseString("<doc>a</doc>")
1190 elem = doc.documentElement
1191 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001192 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001193
1194 self.checkWholeText(text, "a")
1195 elem.appendChild(doc.createTextNode("b"))
1196 self.checkWholeText(text, "ab")
1197 elem.insertBefore(doc.createCDATASection("c"), text)
1198 self.checkWholeText(text, "cab")
1199
1200 # make sure we don't cross other nodes
1201 splitter = doc.createComment("comment")
1202 elem.appendChild(splitter)
1203 text2 = doc.createTextNode("d")
1204 elem.appendChild(text2)
1205 self.checkWholeText(text, "cab")
1206 self.checkWholeText(text2, "d")
1207
1208 x = doc.createElement("x")
1209 elem.replaceChild(x, splitter)
1210 splitter = x
1211 self.checkWholeText(text, "cab")
1212 self.checkWholeText(text2, "d")
1213
1214 x = doc.createProcessingInstruction("y", "z")
1215 elem.replaceChild(x, splitter)
1216 splitter = x
1217 self.checkWholeText(text, "cab")
1218 self.checkWholeText(text2, "d")
1219
1220 elem.removeChild(splitter)
1221 self.checkWholeText(text, "cabd")
1222 self.checkWholeText(text2, "cabd")
1223
1224 def testPatch1094164(self):
1225 doc = parseString("<doc><e/></doc>")
1226 elem = doc.documentElement
1227 e = elem.firstChild
1228 self.confirm(e.parentNode is elem, "Before replaceChild()")
1229 # Check that replacing a child with itself leaves the tree unchanged
1230 elem.replaceChild(e, e)
1231 self.confirm(e.parentNode is elem, "After replaceChild()")
1232
1233 def testReplaceWholeText(self):
1234 def setup():
1235 doc = parseString("<doc>a<e/>d</doc>")
1236 elem = doc.documentElement
1237 text1 = elem.firstChild
1238 text2 = elem.lastChild
1239 splitter = text1.nextSibling
1240 elem.insertBefore(doc.createTextNode("b"), splitter)
1241 elem.insertBefore(doc.createCDATASection("c"), text1)
1242 return doc, elem, text1, splitter, text2
1243
1244 doc, elem, text1, splitter, text2 = setup()
1245 text = text1.replaceWholeText("new content")
1246 self.checkWholeText(text, "new content")
1247 self.checkWholeText(text2, "d")
1248 self.confirm(len(elem.childNodes) == 3)
1249
1250 doc, elem, text1, splitter, text2 = setup()
1251 text = text2.replaceWholeText("new content")
1252 self.checkWholeText(text, "new content")
1253 self.checkWholeText(text1, "cab")
1254 self.confirm(len(elem.childNodes) == 5)
1255
1256 doc, elem, text1, splitter, text2 = setup()
1257 text = text1.replaceWholeText("")
1258 self.checkWholeText(text2, "d")
1259 self.confirm(text is None
1260 and len(elem.childNodes) == 2)
1261
1262 def testSchemaType(self):
1263 doc = parseString(
1264 "<!DOCTYPE doc [\n"
1265 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1266 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1267 " <!ATTLIST doc id ID #IMPLIED \n"
1268 " ref IDREF #IMPLIED \n"
1269 " refs IDREFS #IMPLIED \n"
1270 " enum (a|b) #IMPLIED \n"
1271 " ent ENTITY #IMPLIED \n"
1272 " ents ENTITIES #IMPLIED \n"
1273 " nm NMTOKEN #IMPLIED \n"
1274 " nms NMTOKENS #IMPLIED \n"
1275 " text CDATA #IMPLIED \n"
1276 " >\n"
1277 "]><doc id='name' notid='name' text='splat!' enum='b'"
1278 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1279 " nm='123' nms='123 abc' />")
1280 elem = doc.documentElement
1281 # We don't want to rely on any specific loader at this point, so
1282 # just make sure we can get to all the names, and that the
1283 # DTD-based namespace is right. The names can vary by loader
1284 # since each supports a different level of DTD information.
1285 t = elem.schemaType
1286 self.confirm(t.name is None
1287 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1288 names = "id notid text enum ref refs ent ents nm nms".split()
1289 for name in names:
1290 a = elem.getAttributeNode(name)
1291 t = a.schemaType
1292 self.confirm(hasattr(t, "name")
1293 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1294
1295 def testSetIdAttribute(self):
1296 doc = parseString("<doc a1='v' a2='w'/>")
1297 e = doc.documentElement
1298 a1 = e.getAttributeNode("a1")
1299 a2 = e.getAttributeNode("a2")
1300 self.confirm(doc.getElementById("v") is None
1301 and not a1.isId
1302 and not a2.isId)
1303 e.setIdAttribute("a1")
1304 self.confirm(e.isSameNode(doc.getElementById("v"))
1305 and a1.isId
1306 and not a2.isId)
1307 e.setIdAttribute("a2")
1308 self.confirm(e.isSameNode(doc.getElementById("v"))
1309 and e.isSameNode(doc.getElementById("w"))
1310 and a1.isId
1311 and a2.isId)
1312 # replace the a1 node; the new node should *not* be an ID
1313 a3 = doc.createAttribute("a1")
1314 a3.value = "v"
1315 e.setAttributeNode(a3)
1316 self.confirm(doc.getElementById("v") is None
1317 and e.isSameNode(doc.getElementById("w"))
1318 and not a1.isId
1319 and a2.isId
1320 and not a3.isId)
1321 # renaming an attribute should not affect its ID-ness:
1322 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1323 self.confirm(e.isSameNode(doc.getElementById("w"))
1324 and a2.isId)
1325
1326 def testSetIdAttributeNS(self):
1327 NS1 = "http://xml.python.org/ns1"
1328 NS2 = "http://xml.python.org/ns2"
1329 doc = parseString("<doc"
1330 " xmlns:ns1='" + NS1 + "'"
1331 " xmlns:ns2='" + NS2 + "'"
1332 " ns1:a1='v' ns2:a2='w'/>")
1333 e = doc.documentElement
1334 a1 = e.getAttributeNodeNS(NS1, "a1")
1335 a2 = e.getAttributeNodeNS(NS2, "a2")
1336 self.confirm(doc.getElementById("v") is None
1337 and not a1.isId
1338 and not a2.isId)
1339 e.setIdAttributeNS(NS1, "a1")
1340 self.confirm(e.isSameNode(doc.getElementById("v"))
1341 and a1.isId
1342 and not a2.isId)
1343 e.setIdAttributeNS(NS2, "a2")
1344 self.confirm(e.isSameNode(doc.getElementById("v"))
1345 and e.isSameNode(doc.getElementById("w"))
1346 and a1.isId
1347 and a2.isId)
1348 # replace the a1 node; the new node should *not* be an ID
1349 a3 = doc.createAttributeNS(NS1, "a1")
1350 a3.value = "v"
1351 e.setAttributeNode(a3)
1352 self.confirm(e.isSameNode(doc.getElementById("w")))
1353 self.confirm(not a1.isId)
1354 self.confirm(a2.isId)
1355 self.confirm(not a3.isId)
1356 self.confirm(doc.getElementById("v") is None)
1357 # renaming an attribute should not affect its ID-ness:
1358 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1359 self.confirm(e.isSameNode(doc.getElementById("w"))
1360 and a2.isId)
1361
1362 def testSetIdAttributeNode(self):
1363 NS1 = "http://xml.python.org/ns1"
1364 NS2 = "http://xml.python.org/ns2"
1365 doc = parseString("<doc"
1366 " xmlns:ns1='" + NS1 + "'"
1367 " xmlns:ns2='" + NS2 + "'"
1368 " ns1:a1='v' ns2:a2='w'/>")
1369 e = doc.documentElement
1370 a1 = e.getAttributeNodeNS(NS1, "a1")
1371 a2 = e.getAttributeNodeNS(NS2, "a2")
1372 self.confirm(doc.getElementById("v") is None
1373 and not a1.isId
1374 and not a2.isId)
1375 e.setIdAttributeNode(a1)
1376 self.confirm(e.isSameNode(doc.getElementById("v"))
1377 and a1.isId
1378 and not a2.isId)
1379 e.setIdAttributeNode(a2)
1380 self.confirm(e.isSameNode(doc.getElementById("v"))
1381 and e.isSameNode(doc.getElementById("w"))
1382 and a1.isId
1383 and a2.isId)
1384 # replace the a1 node; the new node should *not* be an ID
1385 a3 = doc.createAttributeNS(NS1, "a1")
1386 a3.value = "v"
1387 e.setAttributeNode(a3)
1388 self.confirm(e.isSameNode(doc.getElementById("w")))
1389 self.confirm(not a1.isId)
1390 self.confirm(a2.isId)
1391 self.confirm(not a3.isId)
1392 self.confirm(doc.getElementById("v") is None)
1393 # renaming an attribute should not affect its ID-ness:
1394 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1395 self.confirm(e.isSameNode(doc.getElementById("w"))
1396 and a2.isId)
1397
1398 def testPickledDocument(self):
1399 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1400 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1401 " 'http://xml.python.org/system' [\n"
1402 " <!ELEMENT e EMPTY>\n"
1403 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1404 "]><doc attr='value'> text\n"
1405 "<?pi sample?> <!-- comment --> <e/> </doc>")
1406 s = pickle.dumps(doc)
1407 doc2 = pickle.loads(s)
1408 stack = [(doc, doc2)]
1409 while stack:
1410 n1, n2 = stack.pop()
1411 self.confirm(n1.nodeType == n2.nodeType
1412 and len(n1.childNodes) == len(n2.childNodes)
1413 and n1.nodeName == n2.nodeName
1414 and not n1.isSameNode(n2)
1415 and not n2.isSameNode(n1))
1416 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1417 len(n1.entities)
1418 len(n2.entities)
1419 len(n1.notations)
1420 len(n2.notations)
1421 self.confirm(len(n1.entities) == len(n2.entities)
1422 and len(n1.notations) == len(n2.notations))
1423 for i in range(len(n1.notations)):
Georg Brandl89fad142010-03-14 10:23:39 +00001424 # XXX this loop body doesn't seem to be executed?
Guido van Rossumd8faa362007-04-27 19:54:29 +00001425 no1 = n1.notations.item(i)
1426 no2 = n1.notations.item(i)
1427 self.confirm(no1.name == no2.name
1428 and no1.publicId == no2.publicId
1429 and no1.systemId == no2.systemId)
Georg Brandl89fad142010-03-14 10:23:39 +00001430 stack.append((no1, no2))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001431 for i in range(len(n1.entities)):
1432 e1 = n1.entities.item(i)
1433 e2 = n2.entities.item(i)
1434 self.confirm(e1.notationName == e2.notationName
1435 and e1.publicId == e2.publicId
1436 and e1.systemId == e2.systemId)
1437 stack.append((e1, e2))
1438 if n1.nodeType != Node.DOCUMENT_NODE:
1439 self.confirm(n1.ownerDocument.isSameNode(doc)
1440 and n2.ownerDocument.isSameNode(doc2))
1441 for i in range(len(n1.childNodes)):
1442 stack.append((n1.childNodes[i], n2.childNodes[i]))
1443
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001444 def testSerializeCommentNodeWithDoubleHyphen(self):
1445 doc = create_doc_without_doctype()
1446 doc.appendChild(doc.createComment("foo--bar"))
1447 self.assertRaises(ValueError, doc.toxml)
1448
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001449
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001450 def testEmptyXMLNSValue(self):
1451 doc = parseString("<element xmlns=''>\n"
1452 "<foo/>\n</element>")
1453 doc2 = parseString(doc.toxml())
1454 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1455
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001456 def testDocRemoveChild(self):
1457 doc = parse(tstfile)
1458 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1459 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1460 num_children_before = len(doc.childNodes)
1461 doc.removeChild(doc.childNodes[0])
1462 num_children_after = len(doc.childNodes)
1463 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001464
Guido van Rossumd8faa362007-04-27 19:54:29 +00001465def test_main():
1466 run_unittest(MinidomTest)
1467
1468if __name__ == "__main__":
1469 test_main()