blob: e8ca497144cce3d848ae2fb7b0e673bb65ce6ee0 [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
Zachary Ware38c707e2015-04-13 15:00:43 -05004from test.support import 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
Yury Selivanov75445082015-05-11 22:57:16 -040052 def testDocumentAsyncAttr(self):
53 doc = Document()
54 self.assertFalse(doc.async_)
55 with self.assertWarns(DeprecationWarning):
56 self.assertFalse(getattr(doc, 'async', True))
57 with self.assertWarns(DeprecationWarning):
58 setattr(doc, 'async', True)
59 with self.assertWarns(DeprecationWarning):
60 self.assertTrue(getattr(doc, 'async', False))
61 self.assertTrue(doc.async_)
62
63 self.assertFalse(Document.async_)
64 with self.assertWarns(DeprecationWarning):
65 self.assertFalse(getattr(Document, 'async', True))
66
Serhiy Storchakaaa9563c2015-04-02 20:55:59 +030067 def testParseFromBinaryFile(self):
68 with open(tstfile, 'rb') as file:
69 dom = parse(file)
70 dom.unlink()
71 self.confirm(isinstance(dom, Document))
72
73 def testParseFromTextFile(self):
74 with open(tstfile, 'r', encoding='iso-8859-1') as file:
Brett Cannonc5246922010-10-30 00:14:59 +000075 dom = parse(file)
76 dom.unlink()
77 self.confirm(isinstance(dom, Document))
Guido van Rossumd8faa362007-04-27 19:54:29 +000078
79 def testGetElementsByTagName(self):
80 dom = parse(tstfile)
81 self.confirm(dom.getElementsByTagName("LI") == \
82 dom.documentElement.getElementsByTagName("LI"))
83 dom.unlink()
84
85 def testInsertBefore(self):
86 dom = parseString("<doc><foo/></doc>")
87 root = dom.documentElement
88 elem = root.childNodes[0]
89 nelem = dom.createElement("element")
90 root.insertBefore(nelem, elem)
91 self.confirm(len(root.childNodes) == 2
92 and root.childNodes.length == 2
93 and root.childNodes[0] is nelem
94 and root.childNodes.item(0) is nelem
95 and root.childNodes[1] is elem
96 and root.childNodes.item(1) is elem
97 and root.firstChild is nelem
98 and root.lastChild is elem
99 and root.toxml() == "<doc><element/><foo/></doc>"
100 , "testInsertBefore -- node properly placed in tree")
101 nelem = dom.createElement("element")
102 root.insertBefore(nelem, None)
103 self.confirm(len(root.childNodes) == 3
104 and root.childNodes.length == 3
105 and root.childNodes[1] is elem
106 and root.childNodes.item(1) is elem
107 and root.childNodes[2] is nelem
108 and root.childNodes.item(2) is nelem
109 and root.lastChild is nelem
110 and nelem.previousSibling is elem
111 and root.toxml() == "<doc><element/><foo/><element/></doc>"
112 , "testInsertBefore -- node properly placed in tree")
113 nelem2 = dom.createElement("bar")
114 root.insertBefore(nelem2, nelem)
115 self.confirm(len(root.childNodes) == 4
116 and root.childNodes.length == 4
117 and root.childNodes[2] is nelem2
118 and root.childNodes.item(2) is nelem2
119 and root.childNodes[3] is nelem
120 and root.childNodes.item(3) is nelem
121 and nelem2.nextSibling is nelem
122 and nelem.previousSibling is nelem2
123 and root.toxml() ==
124 "<doc><element/><foo/><bar/><element/></doc>"
125 , "testInsertBefore -- node properly placed in tree")
126 dom.unlink()
127
128 def _create_fragment_test_nodes(self):
129 dom = parseString("<doc/>")
130 orig = dom.createTextNode("original")
131 c1 = dom.createTextNode("foo")
132 c2 = dom.createTextNode("bar")
133 c3 = dom.createTextNode("bat")
134 dom.documentElement.appendChild(orig)
135 frag = dom.createDocumentFragment()
136 frag.appendChild(c1)
137 frag.appendChild(c2)
138 frag.appendChild(c3)
139 return dom, orig, c1, c2, c3, frag
140
141 def testInsertBeforeFragment(self):
142 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
143 dom.documentElement.insertBefore(frag, None)
144 self.confirm(tuple(dom.documentElement.childNodes) ==
145 (orig, c1, c2, c3),
146 "insertBefore(<fragment>, None)")
147 frag.unlink()
148 dom.unlink()
149
150 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
151 dom.documentElement.insertBefore(frag, orig)
152 self.confirm(tuple(dom.documentElement.childNodes) ==
153 (c1, c2, c3, orig),
154 "insertBefore(<fragment>, orig)")
155 frag.unlink()
156 dom.unlink()
157
158 def testAppendChild(self):
159 dom = parse(tstfile)
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000160 dom.documentElement.appendChild(dom.createComment("Hello"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000161 self.confirm(dom.documentElement.childNodes[-1].nodeName == "#comment")
162 self.confirm(dom.documentElement.childNodes[-1].data == "Hello")
163 dom.unlink()
164
165 def testAppendChildFragment(self):
166 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
167 dom.documentElement.appendChild(frag)
168 self.confirm(tuple(dom.documentElement.childNodes) ==
169 (orig, c1, c2, c3),
170 "appendChild(<fragment>)")
171 frag.unlink()
172 dom.unlink()
173
174 def testReplaceChildFragment(self):
175 dom, orig, c1, c2, c3, frag = self._create_fragment_test_nodes()
176 dom.documentElement.replaceChild(frag, orig)
177 orig.unlink()
178 self.confirm(tuple(dom.documentElement.childNodes) == (c1, c2, c3),
179 "replaceChild(<fragment>)")
180 frag.unlink()
181 dom.unlink()
182
183 def testLegalChildren(self):
184 dom = Document()
185 elem = dom.createElement('element')
186 text = dom.createTextNode('text')
187 self.assertRaises(xml.dom.HierarchyRequestErr, dom.appendChild, text)
188
189 dom.appendChild(elem)
190 self.assertRaises(xml.dom.HierarchyRequestErr, dom.insertBefore, text,
191 elem)
192 self.assertRaises(xml.dom.HierarchyRequestErr, dom.replaceChild, text,
193 elem)
194
195 nodemap = elem.attributes
196 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItem,
197 text)
198 self.assertRaises(xml.dom.HierarchyRequestErr, nodemap.setNamedItemNS,
199 text)
200
201 elem.appendChild(text)
202 dom.unlink()
203
204 def testNamedNodeMapSetItem(self):
205 dom = Document()
206 elem = dom.createElement('element')
207 attrs = elem.attributes
208 attrs["foo"] = "bar"
209 a = attrs.item(0)
210 self.confirm(a.ownerDocument is dom,
211 "NamedNodeMap.__setitem__() sets ownerDocument")
212 self.confirm(a.ownerElement is elem,
213 "NamedNodeMap.__setitem__() sets ownerElement")
214 self.confirm(a.value == "bar",
215 "NamedNodeMap.__setitem__() sets value")
216 self.confirm(a.nodeValue == "bar",
217 "NamedNodeMap.__setitem__() sets nodeValue")
218 elem.unlink()
219 dom.unlink()
220
221 def testNonZero(self):
222 dom = parse(tstfile)
223 self.confirm(dom)# should not be zero
224 dom.appendChild(dom.createComment("foo"))
225 self.confirm(not dom.childNodes[-1].childNodes)
226 dom.unlink()
227
228 def testUnlink(self):
229 dom = parse(tstfile)
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000230 self.assertTrue(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000231 dom.unlink()
Kristján Valur Jónsson17173cf2010-06-09 08:13:42 +0000232 self.assertFalse(dom.childNodes)
233
234 def testContext(self):
235 with parse(tstfile) as dom:
236 self.assertTrue(dom.childNodes)
237 self.assertFalse(dom.childNodes)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000238
239 def testElement(self):
240 dom = Document()
241 dom.appendChild(dom.createElement("abc"))
242 self.confirm(dom.documentElement)
243 dom.unlink()
244
245 def testAAA(self):
246 dom = parseString("<abc/>")
247 el = dom.documentElement
248 el.setAttribute("spam", "jam2")
249 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAA")
250 a = el.getAttributeNode("spam")
251 self.confirm(a.ownerDocument is dom,
252 "setAttribute() sets ownerDocument")
253 self.confirm(a.ownerElement is dom.documentElement,
254 "setAttribute() sets ownerElement")
255 dom.unlink()
256
257 def testAAB(self):
258 dom = parseString("<abc/>")
259 el = dom.documentElement
260 el.setAttribute("spam", "jam")
261 el.setAttribute("spam", "jam2")
262 self.confirm(el.toxml() == '<abc spam="jam2"/>', "testAAB")
263 dom.unlink()
264
265 def testAddAttr(self):
266 dom = Document()
267 child = dom.appendChild(dom.createElement("abc"))
268
269 child.setAttribute("def", "ghi")
270 self.confirm(child.getAttribute("def") == "ghi")
271 self.confirm(child.attributes["def"].value == "ghi")
272
273 child.setAttribute("jkl", "mno")
274 self.confirm(child.getAttribute("jkl") == "mno")
275 self.confirm(child.attributes["jkl"].value == "mno")
276
277 self.confirm(len(child.attributes) == 2)
278
279 child.setAttribute("def", "newval")
280 self.confirm(child.getAttribute("def") == "newval")
281 self.confirm(child.attributes["def"].value == "newval")
282
283 self.confirm(len(child.attributes) == 2)
284 dom.unlink()
285
286 def testDeleteAttr(self):
287 dom = Document()
288 child = dom.appendChild(dom.createElement("abc"))
289
290 self.confirm(len(child.attributes) == 0)
291 child.setAttribute("def", "ghi")
292 self.confirm(len(child.attributes) == 1)
293 del child.attributes["def"]
294 self.confirm(len(child.attributes) == 0)
295 dom.unlink()
296
297 def testRemoveAttr(self):
298 dom = Document()
299 child = dom.appendChild(dom.createElement("abc"))
300
301 child.setAttribute("def", "ghi")
302 self.confirm(len(child.attributes) == 1)
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200303 self.assertRaises(xml.dom.NotFoundErr, child.removeAttribute, "foo")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000304 child.removeAttribute("def")
305 self.confirm(len(child.attributes) == 0)
306 dom.unlink()
307
308 def testRemoveAttrNS(self):
309 dom = Document()
310 child = dom.appendChild(
311 dom.createElementNS("http://www.python.org", "python:abc"))
312 child.setAttributeNS("http://www.w3.org", "xmlns:python",
313 "http://www.python.org")
314 child.setAttributeNS("http://www.python.org", "python:abcattr", "foo")
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200315 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNS,
316 "foo", "http://www.python.org")
Guido van Rossumd8faa362007-04-27 19:54:29 +0000317 self.confirm(len(child.attributes) == 2)
318 child.removeAttributeNS("http://www.python.org", "abcattr")
319 self.confirm(len(child.attributes) == 1)
320 dom.unlink()
321
322 def testRemoveAttributeNode(self):
323 dom = Document()
324 child = dom.appendChild(dom.createElement("foo"))
325 child.setAttribute("spam", "jam")
326 self.confirm(len(child.attributes) == 1)
327 node = child.getAttributeNode("spam")
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200328 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNode,
329 None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000330 child.removeAttributeNode(node)
331 self.confirm(len(child.attributes) == 0
332 and child.getAttributeNode("spam") is None)
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200333 dom2 = Document()
Ezio Melotti0f12be12013-08-10 18:30:29 +0300334 child2 = dom2.appendChild(dom2.createElement("foo"))
335 node2 = child2.getAttributeNode("spam")
336 self.assertRaises(xml.dom.NotFoundErr, child2.removeAttributeNode,
337 node2)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000338 dom.unlink()
339
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200340 def testHasAttribute(self):
341 dom = Document()
342 child = dom.appendChild(dom.createElement("foo"))
343 child.setAttribute("spam", "jam")
344 self.confirm(child.hasAttribute("spam"))
345
Guido van Rossumd8faa362007-04-27 19:54:29 +0000346 def testChangeAttr(self):
347 dom = parseString("<abc/>")
348 el = dom.documentElement
349 el.setAttribute("spam", "jam")
350 self.confirm(len(el.attributes) == 1)
351 el.setAttribute("spam", "bam")
352 # Set this attribute to be an ID and make sure that doesn't change
353 # when changing the value:
354 el.setIdAttribute("spam")
355 self.confirm(len(el.attributes) == 1
356 and el.attributes["spam"].value == "bam"
357 and el.attributes["spam"].nodeValue == "bam"
358 and el.getAttribute("spam") == "bam"
359 and el.getAttributeNode("spam").isId)
360 el.attributes["spam"] = "ham"
361 self.confirm(len(el.attributes) == 1
362 and el.attributes["spam"].value == "ham"
363 and el.attributes["spam"].nodeValue == "ham"
364 and el.getAttribute("spam") == "ham"
365 and el.attributes["spam"].isId)
366 el.setAttribute("spam2", "bam")
367 self.confirm(len(el.attributes) == 2
368 and el.attributes["spam"].value == "ham"
369 and el.attributes["spam"].nodeValue == "ham"
370 and el.getAttribute("spam") == "ham"
371 and el.attributes["spam2"].value == "bam"
372 and el.attributes["spam2"].nodeValue == "bam"
373 and el.getAttribute("spam2") == "bam")
374 el.attributes["spam2"] = "bam2"
375 self.confirm(len(el.attributes) == 2
376 and el.attributes["spam"].value == "ham"
377 and el.attributes["spam"].nodeValue == "ham"
378 and el.getAttribute("spam") == "ham"
379 and el.attributes["spam2"].value == "bam2"
380 and el.attributes["spam2"].nodeValue == "bam2"
381 and el.getAttribute("spam2") == "bam2")
382 dom.unlink()
383
384 def testGetAttrList(self):
385 pass
386
Martin v. Löwis67245a62012-03-05 07:01:49 +0100387 def testGetAttrValues(self):
388 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000389
Martin v. Löwis67245a62012-03-05 07:01:49 +0100390 def testGetAttrLength(self):
391 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000392
Martin v. Löwis67245a62012-03-05 07:01:49 +0100393 def testGetAttribute(self):
394 dom = Document()
395 child = dom.appendChild(
396 dom.createElementNS("http://www.python.org", "python:abc"))
397 self.assertEqual(child.getAttribute('missing'), '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000398
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200399 def testGetAttributeNS(self):
400 dom = Document()
401 child = dom.appendChild(
402 dom.createElementNS("http://www.python.org", "python:abc"))
403 child.setAttributeNS("http://www.w3.org", "xmlns:python",
404 "http://www.python.org")
405 self.assertEqual(child.getAttributeNS("http://www.w3.org", "python"),
406 'http://www.python.org')
407 self.assertEqual(child.getAttributeNS("http://www.w3.org", "other"),
408 '')
Martin v. Löwis67245a62012-03-05 07:01:49 +0100409 child2 = child.appendChild(dom.createElement('abc'))
410 self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"),
411 '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000412
413 def testGetAttributeNode(self): pass
414
415 def testGetElementsByTagNameNS(self):
416 d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'>
417 <minidom:myelem/>
418 </foo>"""
419 dom = parseString(d)
420 elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom",
421 "myelem")
422 self.confirm(len(elems) == 1
423 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom"
424 and elems[0].localName == "myelem"
425 and elems[0].prefix == "minidom"
426 and elems[0].tagName == "minidom:myelem"
427 and elems[0].nodeName == "minidom:myelem")
428 dom.unlink()
429
430 def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri,
431 lname):
432 nodelist = doc.getElementsByTagNameNS(nsuri, lname)
433 self.confirm(len(nodelist) == 0)
434
435 def testGetEmptyNodeListFromElementsByTagNameNS(self):
436 doc = parseString('<doc/>')
437 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
438 doc, 'http://xml.python.org/namespaces/a', 'localname')
439 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
440 doc, '*', 'splat')
441 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
442 doc, 'http://xml.python.org/namespaces/a', '*')
443
444 doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>')
445 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
446 doc, "http://xml.python.org/splat", "not-there")
447 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
448 doc, "*", "not-there")
449 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
450 doc, "http://somewhere.else.net/not-there", "e")
451
452 def testElementReprAndStr(self):
453 dom = Document()
454 el = dom.appendChild(dom.createElement("abc"))
455 string1 = repr(el)
456 string2 = str(el)
457 self.confirm(string1 == string2)
458 dom.unlink()
459
460 def testElementReprAndStrUnicode(self):
461 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000462 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000463 string1 = repr(el)
464 string2 = str(el)
465 self.confirm(string1 == string2)
466 dom.unlink()
467
468 def testElementReprAndStrUnicodeNS(self):
469 dom = Document()
470 el = dom.appendChild(
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000471 dom.createElementNS("http://www.slashdot.org", "slash:abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000472 string1 = repr(el)
473 string2 = str(el)
474 self.confirm(string1 == string2)
Ezio Melotti7fb4da72010-03-18 12:29:13 +0000475 self.confirm("slash:abc" in string1)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000476 dom.unlink()
477
478 def testAttributeRepr(self):
479 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000480 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000481 node = el.setAttribute("abc", "def")
482 self.confirm(str(node) == repr(node))
483 dom.unlink()
484
485 def testTextNodeRepr(self): pass
486
487 def testWriteXML(self):
488 str = '<?xml version="1.0" ?><a b="c"/>'
489 dom = parseString(str)
490 domstr = dom.toxml()
491 dom.unlink()
492 self.confirm(str == domstr)
493
494 def testAltNewline(self):
495 str = '<?xml version="1.0" ?>\n<a b="c"/>\n'
496 dom = parseString(str)
497 domstr = dom.toprettyxml(newl="\r\n")
498 dom.unlink()
499 self.confirm(domstr == str.replace("\n", "\r\n"))
500
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200501 def test_toprettyxml_with_text_nodes(self):
502 # see issue #4147, text nodes are not indented
503 decl = '<?xml version="1.0" ?>\n'
504 self.assertEqual(parseString('<B>A</B>').toprettyxml(),
505 decl + '<B>A</B>\n')
506 self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
507 decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
508 self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
509 decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
510 self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
511 decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
512 self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
513 decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
514
515 def test_toprettyxml_with_adjacent_text_nodes(self):
516 # see issue #4147, adjacent text nodes are indented normally
517 dom = Document()
518 elem = dom.createElement('elem')
519 elem.appendChild(dom.createTextNode('TEXT'))
520 elem.appendChild(dom.createTextNode('TEXT'))
521 dom.appendChild(elem)
522 decl = '<?xml version="1.0" ?>\n'
523 self.assertEqual(dom.toprettyxml(),
524 decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
525
Éric Araujo63ba97b2011-10-05 01:29:22 +0200526 def test_toprettyxml_preserves_content_of_text_node(self):
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200527 # see issue #4147
528 for str in ('<B>A</B>', '<A><B>C</B></A>'):
529 dom = parseString(str)
530 dom2 = parseString(dom.toprettyxml())
531 self.assertEqual(
532 dom.getElementsByTagName('B')[0].childNodes[0].toxml(),
533 dom2.getElementsByTagName('B')[0].childNodes[0].toxml())
R David Murray791744b2011-10-01 16:19:51 -0400534
Guido van Rossumd8faa362007-04-27 19:54:29 +0000535 def testProcessingInstruction(self):
536 dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
537 pi = dom.documentElement.firstChild
538 self.confirm(pi.target == "mypi"
539 and pi.data == "data \t\n "
540 and pi.nodeName == "mypi"
541 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
542 and pi.attributes is None
543 and not pi.hasChildNodes()
544 and len(pi.childNodes) == 0
545 and pi.firstChild is None
546 and pi.lastChild is None
547 and pi.localName is None
548 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
549
550 def testProcessingInstructionRepr(self): pass
551
552 def testTextRepr(self): pass
553
554 def testWriteText(self): pass
555
556 def testDocumentElement(self): pass
557
558 def testTooManyDocumentElements(self):
559 doc = parseString("<doc/>")
560 elem = doc.createElement("extra")
561 # Should raise an exception when adding an extra document element.
562 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
563 elem.unlink()
564 doc.unlink()
565
566 def testCreateElementNS(self): pass
567
568 def testCreateAttributeNS(self): pass
569
570 def testParse(self): pass
571
572 def testParseString(self): pass
573
574 def testComment(self): pass
575
576 def testAttrListItem(self): pass
577
578 def testAttrListItems(self): pass
579
580 def testAttrListItemNS(self): pass
581
582 def testAttrListKeys(self): pass
583
584 def testAttrListKeysNS(self): pass
585
586 def testRemoveNamedItem(self):
587 doc = parseString("<doc a=''/>")
588 e = doc.documentElement
589 attrs = e.attributes
590 a1 = e.getAttributeNode("a")
591 a2 = attrs.removeNamedItem("a")
592 self.confirm(a1.isSameNode(a2))
593 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
594
595 def testRemoveNamedItemNS(self):
596 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
597 e = doc.documentElement
598 attrs = e.attributes
599 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
600 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
601 self.confirm(a1.isSameNode(a2))
602 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
603 "http://xml.python.org/", "b")
604
605 def testAttrListValues(self): pass
606
607 def testAttrListLength(self): pass
608
609 def testAttrList__getitem__(self): pass
610
611 def testAttrList__setitem__(self): pass
612
613 def testSetAttrValueandNodeValue(self): pass
614
615 def testParseElement(self): pass
616
617 def testParseAttributes(self): pass
618
619 def testParseElementNamespaces(self): pass
620
621 def testParseAttributeNamespaces(self): pass
622
623 def testParseProcessingInstructions(self): pass
624
625 def testChildNodes(self): pass
626
627 def testFirstChild(self): pass
628
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200629 def testHasChildNodes(self):
630 dom = parseString("<doc><foo/></doc>")
631 doc = dom.documentElement
Ezio Melotti0f12be12013-08-10 18:30:29 +0300632 self.assertTrue(doc.hasChildNodes())
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200633 dom2 = parseString("<doc/>")
634 doc2 = dom2.documentElement
635 self.assertFalse(doc2.hasChildNodes())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000636
637 def _testCloneElementCopiesAttributes(self, e1, e2, test):
638 attrs1 = e1.attributes
639 attrs2 = e2.attributes
640 keys1 = list(attrs1.keys())
641 keys2 = list(attrs2.keys())
642 keys1.sort()
643 keys2.sort()
644 self.confirm(keys1 == keys2, "clone of element has same attribute keys")
645 for i in range(len(keys1)):
646 a1 = attrs1.item(i)
647 a2 = attrs2.item(i)
648 self.confirm(a1 is not a2
649 and a1.value == a2.value
650 and a1.nodeValue == a2.nodeValue
651 and a1.namespaceURI == a2.namespaceURI
652 and a1.localName == a2.localName
653 , "clone of attribute node has proper attribute values")
654 self.confirm(a2.ownerElement is e2,
655 "clone of attribute node correctly owned")
656
657 def _setupCloneElement(self, deep):
658 dom = parseString("<doc attr='value'><foo/></doc>")
659 root = dom.documentElement
660 clone = root.cloneNode(deep)
661 self._testCloneElementCopiesAttributes(
662 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
663 # mutilate the original so shared data is detected
664 root.tagName = root.nodeName = "MODIFIED"
665 root.setAttribute("attr", "NEW VALUE")
666 root.setAttribute("added", "VALUE")
667 return dom, clone
668
669 def testCloneElementShallow(self):
670 dom, clone = self._setupCloneElement(0)
671 self.confirm(len(clone.childNodes) == 0
672 and clone.childNodes.length == 0
673 and clone.parentNode is None
674 and clone.toxml() == '<doc attr="value"/>'
675 , "testCloneElementShallow")
676 dom.unlink()
677
678 def testCloneElementDeep(self):
679 dom, clone = self._setupCloneElement(1)
680 self.confirm(len(clone.childNodes) == 1
681 and clone.childNodes.length == 1
682 and clone.parentNode is None
683 and clone.toxml() == '<doc attr="value"><foo/></doc>'
684 , "testCloneElementDeep")
685 dom.unlink()
686
687 def testCloneDocumentShallow(self):
688 doc = parseString("<?xml version='1.0'?>\n"
689 "<!-- comment -->"
690 "<!DOCTYPE doc [\n"
691 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
692 "]>\n"
693 "<doc attr='value'/>")
694 doc2 = doc.cloneNode(0)
695 self.confirm(doc2 is None,
696 "testCloneDocumentShallow:"
697 " shallow cloning of documents makes no sense!")
698
699 def testCloneDocumentDeep(self):
700 doc = parseString("<?xml version='1.0'?>\n"
701 "<!-- comment -->"
702 "<!DOCTYPE doc [\n"
703 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
704 "]>\n"
705 "<doc attr='value'/>")
706 doc2 = doc.cloneNode(1)
707 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
708 "testCloneDocumentDeep: document objects not distinct")
709 self.confirm(len(doc.childNodes) == len(doc2.childNodes),
710 "testCloneDocumentDeep: wrong number of Document children")
711 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
712 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
713 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
714 "testCloneDocumentDeep: documentElement owner is not new document")
715 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
716 "testCloneDocumentDeep: documentElement should not be shared")
717 if doc.doctype is not None:
718 # check the doctype iff the original DOM maintained it
719 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
720 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
721 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
722 self.confirm(not doc.doctype.isSameNode(doc2.doctype))
723
724 def testCloneDocumentTypeDeepOk(self):
725 doctype = create_nonempty_doctype()
726 clone = doctype.cloneNode(1)
727 self.confirm(clone is not None
728 and clone.nodeName == doctype.nodeName
729 and clone.name == doctype.name
730 and clone.publicId == doctype.publicId
731 and clone.systemId == doctype.systemId
732 and len(clone.entities) == len(doctype.entities)
733 and clone.entities.item(len(clone.entities)) is None
734 and len(clone.notations) == len(doctype.notations)
735 and clone.notations.item(len(clone.notations)) is None
736 and len(clone.childNodes) == 0)
737 for i in range(len(doctype.entities)):
738 se = doctype.entities.item(i)
739 ce = clone.entities.item(i)
740 self.confirm((not se.isSameNode(ce))
741 and (not ce.isSameNode(se))
742 and ce.nodeName == se.nodeName
743 and ce.notationName == se.notationName
744 and ce.publicId == se.publicId
745 and ce.systemId == se.systemId
746 and ce.encoding == se.encoding
747 and ce.actualEncoding == se.actualEncoding
748 and ce.version == se.version)
749 for i in range(len(doctype.notations)):
750 sn = doctype.notations.item(i)
751 cn = clone.notations.item(i)
752 self.confirm((not sn.isSameNode(cn))
753 and (not cn.isSameNode(sn))
754 and cn.nodeName == sn.nodeName
755 and cn.publicId == sn.publicId
756 and cn.systemId == sn.systemId)
757
758 def testCloneDocumentTypeDeepNotOk(self):
759 doc = create_doc_with_doctype()
760 clone = doc.doctype.cloneNode(1)
761 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
762
763 def testCloneDocumentTypeShallowOk(self):
764 doctype = create_nonempty_doctype()
765 clone = doctype.cloneNode(0)
766 self.confirm(clone is not None
767 and clone.nodeName == doctype.nodeName
768 and clone.name == doctype.name
769 and clone.publicId == doctype.publicId
770 and clone.systemId == doctype.systemId
771 and len(clone.entities) == 0
772 and clone.entities.item(0) is None
773 and len(clone.notations) == 0
774 and clone.notations.item(0) is None
775 and len(clone.childNodes) == 0)
776
777 def testCloneDocumentTypeShallowNotOk(self):
778 doc = create_doc_with_doctype()
779 clone = doc.doctype.cloneNode(0)
780 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
781
782 def check_import_document(self, deep, testName):
783 doc1 = parseString("<doc/>")
784 doc2 = parseString("<doc/>")
785 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
786
787 def testImportDocumentShallow(self):
788 self.check_import_document(0, "testImportDocumentShallow")
789
790 def testImportDocumentDeep(self):
791 self.check_import_document(1, "testImportDocumentDeep")
792
793 def testImportDocumentTypeShallow(self):
794 src = create_doc_with_doctype()
795 target = create_doc_without_doctype()
796 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
797 src.doctype, 0)
798
799 def testImportDocumentTypeDeep(self):
800 src = create_doc_with_doctype()
801 target = create_doc_without_doctype()
802 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
803 src.doctype, 1)
804
805 # Testing attribute clones uses a helper, and should always be deep,
806 # even if the argument to cloneNode is false.
807 def check_clone_attribute(self, deep, testName):
808 doc = parseString("<doc attr='value'/>")
809 attr = doc.documentElement.getAttributeNode("attr")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000810 self.assertNotEqual(attr, None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000811 clone = attr.cloneNode(deep)
812 self.confirm(not clone.isSameNode(attr))
813 self.confirm(not attr.isSameNode(clone))
814 self.confirm(clone.ownerElement is None,
815 testName + ": ownerElement should be None")
816 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
817 testName + ": ownerDocument does not match")
818 self.confirm(clone.specified,
819 testName + ": cloned attribute must have specified == True")
820
821 def testCloneAttributeShallow(self):
822 self.check_clone_attribute(0, "testCloneAttributeShallow")
823
824 def testCloneAttributeDeep(self):
825 self.check_clone_attribute(1, "testCloneAttributeDeep")
826
827 def check_clone_pi(self, deep, testName):
828 doc = parseString("<?target data?><doc/>")
829 pi = doc.firstChild
Ezio Melottib3aedd42010-11-20 19:04:17 +0000830 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000831 clone = pi.cloneNode(deep)
832 self.confirm(clone.target == pi.target
833 and clone.data == pi.data)
834
835 def testClonePIShallow(self):
836 self.check_clone_pi(0, "testClonePIShallow")
837
838 def testClonePIDeep(self):
839 self.check_clone_pi(1, "testClonePIDeep")
840
841 def testNormalize(self):
842 doc = parseString("<doc/>")
843 root = doc.documentElement
844 root.appendChild(doc.createTextNode("first"))
845 root.appendChild(doc.createTextNode("second"))
846 self.confirm(len(root.childNodes) == 2
847 and root.childNodes.length == 2,
848 "testNormalize -- preparation")
849 doc.normalize()
850 self.confirm(len(root.childNodes) == 1
851 and root.childNodes.length == 1
852 and root.firstChild is root.lastChild
853 and root.firstChild.data == "firstsecond"
854 , "testNormalize -- result")
855 doc.unlink()
856
857 doc = parseString("<doc/>")
858 root = doc.documentElement
859 root.appendChild(doc.createTextNode(""))
860 doc.normalize()
861 self.confirm(len(root.childNodes) == 0
862 and root.childNodes.length == 0,
863 "testNormalize -- single empty node removed")
864 doc.unlink()
865
R. David Murraydc6da8a2009-04-09 22:16:43 +0000866 def testNormalizeCombineAndNextSibling(self):
867 doc = parseString("<doc/>")
868 root = doc.documentElement
869 root.appendChild(doc.createTextNode("first"))
870 root.appendChild(doc.createTextNode("second"))
871 root.appendChild(doc.createElement("i"))
872 self.confirm(len(root.childNodes) == 3
873 and root.childNodes.length == 3,
874 "testNormalizeCombineAndNextSibling -- preparation")
875 doc.normalize()
876 self.confirm(len(root.childNodes) == 2
877 and root.childNodes.length == 2
878 and root.firstChild.data == "firstsecond"
879 and root.firstChild is not root.lastChild
880 and root.firstChild.nextSibling is root.lastChild
881 and root.firstChild.previousSibling is None
882 and root.lastChild.previousSibling is root.firstChild
883 and root.lastChild.nextSibling is None
884 , "testNormalizeCombinedAndNextSibling -- result")
885 doc.unlink()
886
887 def testNormalizeDeleteWithPrevSibling(self):
888 doc = parseString("<doc/>")
889 root = doc.documentElement
890 root.appendChild(doc.createTextNode("first"))
891 root.appendChild(doc.createTextNode(""))
892 self.confirm(len(root.childNodes) == 2
893 and root.childNodes.length == 2,
894 "testNormalizeDeleteWithPrevSibling -- preparation")
895 doc.normalize()
896 self.confirm(len(root.childNodes) == 1
897 and root.childNodes.length == 1
898 and root.firstChild.data == "first"
899 and root.firstChild is root.lastChild
900 and root.firstChild.nextSibling is None
901 and root.firstChild.previousSibling is None
902 , "testNormalizeDeleteWithPrevSibling -- result")
903 doc.unlink()
904
905 def testNormalizeDeleteWithNextSibling(self):
906 doc = parseString("<doc/>")
907 root = doc.documentElement
908 root.appendChild(doc.createTextNode(""))
909 root.appendChild(doc.createTextNode("second"))
910 self.confirm(len(root.childNodes) == 2
911 and root.childNodes.length == 2,
912 "testNormalizeDeleteWithNextSibling -- preparation")
913 doc.normalize()
914 self.confirm(len(root.childNodes) == 1
915 and root.childNodes.length == 1
916 and root.firstChild.data == "second"
917 and root.firstChild is root.lastChild
918 and root.firstChild.nextSibling is None
919 and root.firstChild.previousSibling is None
920 , "testNormalizeDeleteWithNextSibling -- result")
921 doc.unlink()
922
923 def testNormalizeDeleteWithTwoNonTextSiblings(self):
924 doc = parseString("<doc/>")
925 root = doc.documentElement
926 root.appendChild(doc.createElement("i"))
927 root.appendChild(doc.createTextNode(""))
928 root.appendChild(doc.createElement("i"))
929 self.confirm(len(root.childNodes) == 3
930 and root.childNodes.length == 3,
931 "testNormalizeDeleteWithTwoSiblings -- preparation")
932 doc.normalize()
933 self.confirm(len(root.childNodes) == 2
934 and root.childNodes.length == 2
935 and root.firstChild is not root.lastChild
936 and root.firstChild.nextSibling is root.lastChild
937 and root.firstChild.previousSibling is None
938 and root.lastChild.previousSibling is root.firstChild
939 and root.lastChild.nextSibling is None
940 , "testNormalizeDeleteWithTwoSiblings -- result")
941 doc.unlink()
942
943 def testNormalizeDeleteAndCombine(self):
944 doc = parseString("<doc/>")
945 root = doc.documentElement
946 root.appendChild(doc.createTextNode(""))
947 root.appendChild(doc.createTextNode("second"))
948 root.appendChild(doc.createTextNode(""))
949 root.appendChild(doc.createTextNode("fourth"))
950 root.appendChild(doc.createTextNode(""))
951 self.confirm(len(root.childNodes) == 5
952 and root.childNodes.length == 5,
953 "testNormalizeDeleteAndCombine -- preparation")
954 doc.normalize()
955 self.confirm(len(root.childNodes) == 1
956 and root.childNodes.length == 1
957 and root.firstChild is root.lastChild
958 and root.firstChild.data == "secondfourth"
959 and root.firstChild.previousSibling is None
960 and root.firstChild.nextSibling is None
961 , "testNormalizeDeleteAndCombine -- result")
962 doc.unlink()
963
964 def testNormalizeRecursion(self):
965 doc = parseString("<doc>"
966 "<o>"
967 "<i/>"
968 "t"
969 #
970 #x
971 "</o>"
972 "<o>"
973 "<o>"
974 "t2"
975 #x2
976 "</o>"
977 "t3"
978 #x3
979 "</o>"
980 #
981 "</doc>")
982 root = doc.documentElement
983 root.childNodes[0].appendChild(doc.createTextNode(""))
984 root.childNodes[0].appendChild(doc.createTextNode("x"))
985 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
986 root.childNodes[1].appendChild(doc.createTextNode("x3"))
987 root.appendChild(doc.createTextNode(""))
988 self.confirm(len(root.childNodes) == 3
989 and root.childNodes.length == 3
990 and len(root.childNodes[0].childNodes) == 4
991 and root.childNodes[0].childNodes.length == 4
992 and len(root.childNodes[1].childNodes) == 3
993 and root.childNodes[1].childNodes.length == 3
994 and len(root.childNodes[1].childNodes[0].childNodes) == 2
995 and root.childNodes[1].childNodes[0].childNodes.length == 2
996 , "testNormalize2 -- preparation")
997 doc.normalize()
998 self.confirm(len(root.childNodes) == 2
999 and root.childNodes.length == 2
1000 and len(root.childNodes[0].childNodes) == 2
1001 and root.childNodes[0].childNodes.length == 2
1002 and len(root.childNodes[1].childNodes) == 2
1003 and root.childNodes[1].childNodes.length == 2
1004 and len(root.childNodes[1].childNodes[0].childNodes) == 1
1005 and root.childNodes[1].childNodes[0].childNodes.length == 1
1006 , "testNormalize2 -- childNodes lengths")
1007 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
1008 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
1009 and root.childNodes[1].childNodes[1].data == "t3x3"
1010 , "testNormalize2 -- joined text fields")
1011 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
1012 and root.childNodes[0].childNodes[1].previousSibling
1013 is root.childNodes[0].childNodes[0]
1014 and root.childNodes[0].childNodes[0].previousSibling is None
1015 and root.childNodes[0].childNodes[0].nextSibling
1016 is root.childNodes[0].childNodes[1]
1017 and root.childNodes[1].childNodes[1].nextSibling is None
1018 and root.childNodes[1].childNodes[1].previousSibling
1019 is root.childNodes[1].childNodes[0]
1020 and root.childNodes[1].childNodes[0].previousSibling is None
1021 and root.childNodes[1].childNodes[0].nextSibling
1022 is root.childNodes[1].childNodes[1]
1023 , "testNormalize2 -- sibling pointers")
1024 doc.unlink()
1025
1026
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001027 def testBug0777884(self):
1028 doc = parseString("<o>text</o>")
1029 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001030 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001031 # Should run quietly, doing nothing.
1032 text.normalize()
1033 doc.unlink()
1034
Christian Heimes05e8be12008-02-23 18:30:17 +00001035 def testBug1433694(self):
1036 doc = parseString("<o><i/>t</o>")
1037 node = doc.documentElement
1038 node.childNodes[1].nodeValue = ""
1039 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +00001040 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +00001041 "Final child's .nextSibling should be None")
1042
Guido van Rossumd8faa362007-04-27 19:54:29 +00001043 def testSiblings(self):
1044 doc = parseString("<doc><?pi?>text?<elm/></doc>")
1045 root = doc.documentElement
1046 (pi, text, elm) = root.childNodes
1047
1048 self.confirm(pi.nextSibling is text and
1049 pi.previousSibling is None and
1050 text.nextSibling is elm and
1051 text.previousSibling is pi and
1052 elm.nextSibling is None and
1053 elm.previousSibling is text, "testSiblings")
1054
1055 doc.unlink()
1056
1057 def testParents(self):
1058 doc = parseString(
1059 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
1060 root = doc.documentElement
1061 elm1 = root.childNodes[0]
1062 (elm2a, elm2b) = elm1.childNodes
1063 elm3 = elm2b.childNodes[0]
1064
1065 self.confirm(root.parentNode is doc and
1066 elm1.parentNode is root and
1067 elm2a.parentNode is elm1 and
1068 elm2b.parentNode is elm1 and
1069 elm3.parentNode is elm2b, "testParents")
1070 doc.unlink()
1071
1072 def testNodeListItem(self):
1073 doc = parseString("<doc><e/><e/></doc>")
1074 children = doc.childNodes
1075 docelem = children[0]
1076 self.confirm(children[0] is children.item(0)
1077 and children.item(1) is None
1078 and docelem.childNodes.item(0) is docelem.childNodes[0]
1079 and docelem.childNodes.item(1) is docelem.childNodes[1]
1080 and docelem.childNodes.item(0).childNodes.item(0) is None,
1081 "test NodeList.item()")
1082 doc.unlink()
1083
Guido van Rossumd8faa362007-04-27 19:54:29 +00001084 def testEncodings(self):
1085 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001086 self.assertEqual(doc.toxml(),
1087 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1088 self.assertEqual(doc.toxml('utf-8'),
1089 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1090 self.assertEqual(doc.toxml('iso-8859-15'),
1091 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Eli Bendersky8a805022012-07-13 09:52:39 +03001092 self.assertEqual(doc.toxml('us-ascii'),
1093 b'<?xml version="1.0" encoding="us-ascii"?><foo>&#8364;</foo>')
1094 self.assertEqual(doc.toxml('utf-16'),
1095 '<?xml version="1.0" encoding="utf-16"?>'
1096 '<foo>\u20ac</foo>'.encode('utf-16'))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001097
Andrew Svetlov737fb892012-12-18 21:14:22 +02001098 # Verify that character decoding errors raise exceptions instead
Guido van Rossumd8faa362007-04-27 19:54:29 +00001099 # of crashing
1100 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001101 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001102
1103 doc.unlink()
1104
1105 class UserDataHandler:
1106 called = 0
1107 def handle(self, operation, key, data, src, dst):
1108 dst.setUserData(key, data + 1, self)
1109 src.setUserData(key, None, None)
1110 self.called = 1
1111
1112 def testUserData(self):
1113 dom = Document()
1114 n = dom.createElement('e')
1115 self.confirm(n.getUserData("foo") is None)
1116 n.setUserData("foo", None, None)
1117 self.confirm(n.getUserData("foo") is None)
1118 n.setUserData("foo", 12, 12)
1119 n.setUserData("bar", 13, 13)
1120 self.confirm(n.getUserData("foo") == 12)
1121 self.confirm(n.getUserData("bar") == 13)
1122 n.setUserData("foo", None, None)
1123 self.confirm(n.getUserData("foo") is None)
1124 self.confirm(n.getUserData("bar") == 13)
1125
1126 handler = self.UserDataHandler()
1127 n.setUserData("bar", 12, handler)
1128 c = n.cloneNode(1)
1129 self.confirm(handler.called
1130 and n.getUserData("bar") is None
1131 and c.getUserData("bar") == 13)
1132 n.unlink()
1133 c.unlink()
1134 dom.unlink()
1135
1136 def checkRenameNodeSharedConstraints(self, doc, node):
1137 # Make sure illegal NS usage is detected:
1138 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1139 "http://xml.python.org/ns", "xmlns:foo")
1140 doc2 = parseString("<doc/>")
1141 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1142 xml.dom.EMPTY_NAMESPACE, "foo")
1143
1144 def testRenameAttribute(self):
1145 doc = parseString("<doc a='v'/>")
1146 elem = doc.documentElement
1147 attrmap = elem.attributes
1148 attr = elem.attributes['a']
1149
1150 # Simple renaming
1151 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1152 self.confirm(attr.name == "b"
1153 and attr.nodeName == "b"
1154 and attr.localName is None
1155 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1156 and attr.prefix is None
1157 and attr.value == "v"
1158 and elem.getAttributeNode("a") is None
1159 and elem.getAttributeNode("b").isSameNode(attr)
1160 and attrmap["b"].isSameNode(attr)
1161 and attr.ownerDocument.isSameNode(doc)
1162 and attr.ownerElement.isSameNode(elem))
1163
1164 # Rename to have a namespace, no prefix
1165 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1166 self.confirm(attr.name == "c"
1167 and attr.nodeName == "c"
1168 and attr.localName == "c"
1169 and attr.namespaceURI == "http://xml.python.org/ns"
1170 and attr.prefix is None
1171 and attr.value == "v"
1172 and elem.getAttributeNode("a") is None
1173 and elem.getAttributeNode("b") is None
1174 and elem.getAttributeNode("c").isSameNode(attr)
1175 and elem.getAttributeNodeNS(
1176 "http://xml.python.org/ns", "c").isSameNode(attr)
1177 and attrmap["c"].isSameNode(attr)
1178 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1179
1180 # Rename to have a namespace, with prefix
1181 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1182 self.confirm(attr.name == "p:d"
1183 and attr.nodeName == "p:d"
1184 and attr.localName == "d"
1185 and attr.namespaceURI == "http://xml.python.org/ns2"
1186 and attr.prefix == "p"
1187 and attr.value == "v"
1188 and elem.getAttributeNode("a") is None
1189 and elem.getAttributeNode("b") is None
1190 and elem.getAttributeNode("c") is None
1191 and elem.getAttributeNodeNS(
1192 "http://xml.python.org/ns", "c") is None
1193 and elem.getAttributeNode("p:d").isSameNode(attr)
1194 and elem.getAttributeNodeNS(
1195 "http://xml.python.org/ns2", "d").isSameNode(attr)
1196 and attrmap["p:d"].isSameNode(attr)
1197 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1198
1199 # Rename back to a simple non-NS node
1200 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1201 self.confirm(attr.name == "e"
1202 and attr.nodeName == "e"
1203 and attr.localName is None
1204 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1205 and attr.prefix is None
1206 and attr.value == "v"
1207 and elem.getAttributeNode("a") is None
1208 and elem.getAttributeNode("b") is None
1209 and elem.getAttributeNode("c") is None
1210 and elem.getAttributeNode("p:d") is None
1211 and elem.getAttributeNodeNS(
1212 "http://xml.python.org/ns", "c") is None
1213 and elem.getAttributeNode("e").isSameNode(attr)
1214 and attrmap["e"].isSameNode(attr))
1215
1216 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1217 "http://xml.python.org/ns", "xmlns")
1218 self.checkRenameNodeSharedConstraints(doc, attr)
1219 doc.unlink()
1220
1221 def testRenameElement(self):
1222 doc = parseString("<doc/>")
1223 elem = doc.documentElement
1224
1225 # Simple renaming
1226 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1227 self.confirm(elem.tagName == "a"
1228 and elem.nodeName == "a"
1229 and elem.localName is None
1230 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1231 and elem.prefix is None
1232 and elem.ownerDocument.isSameNode(doc))
1233
1234 # Rename to have a namespace, no prefix
1235 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1236 self.confirm(elem.tagName == "b"
1237 and elem.nodeName == "b"
1238 and elem.localName == "b"
1239 and elem.namespaceURI == "http://xml.python.org/ns"
1240 and elem.prefix is None
1241 and elem.ownerDocument.isSameNode(doc))
1242
1243 # Rename to have a namespace, with prefix
1244 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1245 self.confirm(elem.tagName == "p:c"
1246 and elem.nodeName == "p:c"
1247 and elem.localName == "c"
1248 and elem.namespaceURI == "http://xml.python.org/ns2"
1249 and elem.prefix == "p"
1250 and elem.ownerDocument.isSameNode(doc))
1251
1252 # Rename back to a simple non-NS node
1253 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1254 self.confirm(elem.tagName == "d"
1255 and elem.nodeName == "d"
1256 and elem.localName is None
1257 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1258 and elem.prefix is None
1259 and elem.ownerDocument.isSameNode(doc))
1260
1261 self.checkRenameNodeSharedConstraints(doc, elem)
1262 doc.unlink()
1263
1264 def testRenameOther(self):
1265 # We have to create a comment node explicitly since not all DOM
1266 # builders used with minidom add comments to the DOM.
1267 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1268 xml.dom.EMPTY_NAMESPACE, "e", None)
1269 node = doc.createComment("comment")
1270 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1271 xml.dom.EMPTY_NAMESPACE, "foo")
1272 doc.unlink()
1273
1274 def testWholeText(self):
1275 doc = parseString("<doc>a</doc>")
1276 elem = doc.documentElement
1277 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001278 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001279
1280 self.checkWholeText(text, "a")
1281 elem.appendChild(doc.createTextNode("b"))
1282 self.checkWholeText(text, "ab")
1283 elem.insertBefore(doc.createCDATASection("c"), text)
1284 self.checkWholeText(text, "cab")
1285
1286 # make sure we don't cross other nodes
1287 splitter = doc.createComment("comment")
1288 elem.appendChild(splitter)
1289 text2 = doc.createTextNode("d")
1290 elem.appendChild(text2)
1291 self.checkWholeText(text, "cab")
1292 self.checkWholeText(text2, "d")
1293
1294 x = doc.createElement("x")
1295 elem.replaceChild(x, splitter)
1296 splitter = x
1297 self.checkWholeText(text, "cab")
1298 self.checkWholeText(text2, "d")
1299
1300 x = doc.createProcessingInstruction("y", "z")
1301 elem.replaceChild(x, splitter)
1302 splitter = x
1303 self.checkWholeText(text, "cab")
1304 self.checkWholeText(text2, "d")
1305
1306 elem.removeChild(splitter)
1307 self.checkWholeText(text, "cabd")
1308 self.checkWholeText(text2, "cabd")
1309
1310 def testPatch1094164(self):
1311 doc = parseString("<doc><e/></doc>")
1312 elem = doc.documentElement
1313 e = elem.firstChild
1314 self.confirm(e.parentNode is elem, "Before replaceChild()")
1315 # Check that replacing a child with itself leaves the tree unchanged
1316 elem.replaceChild(e, e)
1317 self.confirm(e.parentNode is elem, "After replaceChild()")
1318
1319 def testReplaceWholeText(self):
1320 def setup():
1321 doc = parseString("<doc>a<e/>d</doc>")
1322 elem = doc.documentElement
1323 text1 = elem.firstChild
1324 text2 = elem.lastChild
1325 splitter = text1.nextSibling
1326 elem.insertBefore(doc.createTextNode("b"), splitter)
1327 elem.insertBefore(doc.createCDATASection("c"), text1)
1328 return doc, elem, text1, splitter, text2
1329
1330 doc, elem, text1, splitter, text2 = setup()
1331 text = text1.replaceWholeText("new content")
1332 self.checkWholeText(text, "new content")
1333 self.checkWholeText(text2, "d")
1334 self.confirm(len(elem.childNodes) == 3)
1335
1336 doc, elem, text1, splitter, text2 = setup()
1337 text = text2.replaceWholeText("new content")
1338 self.checkWholeText(text, "new content")
1339 self.checkWholeText(text1, "cab")
1340 self.confirm(len(elem.childNodes) == 5)
1341
1342 doc, elem, text1, splitter, text2 = setup()
1343 text = text1.replaceWholeText("")
1344 self.checkWholeText(text2, "d")
1345 self.confirm(text is None
1346 and len(elem.childNodes) == 2)
1347
1348 def testSchemaType(self):
1349 doc = parseString(
1350 "<!DOCTYPE doc [\n"
1351 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1352 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1353 " <!ATTLIST doc id ID #IMPLIED \n"
1354 " ref IDREF #IMPLIED \n"
1355 " refs IDREFS #IMPLIED \n"
1356 " enum (a|b) #IMPLIED \n"
1357 " ent ENTITY #IMPLIED \n"
1358 " ents ENTITIES #IMPLIED \n"
1359 " nm NMTOKEN #IMPLIED \n"
1360 " nms NMTOKENS #IMPLIED \n"
1361 " text CDATA #IMPLIED \n"
1362 " >\n"
1363 "]><doc id='name' notid='name' text='splat!' enum='b'"
1364 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1365 " nm='123' nms='123 abc' />")
1366 elem = doc.documentElement
1367 # We don't want to rely on any specific loader at this point, so
1368 # just make sure we can get to all the names, and that the
1369 # DTD-based namespace is right. The names can vary by loader
1370 # since each supports a different level of DTD information.
1371 t = elem.schemaType
1372 self.confirm(t.name is None
1373 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1374 names = "id notid text enum ref refs ent ents nm nms".split()
1375 for name in names:
1376 a = elem.getAttributeNode(name)
1377 t = a.schemaType
1378 self.confirm(hasattr(t, "name")
1379 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1380
1381 def testSetIdAttribute(self):
1382 doc = parseString("<doc a1='v' a2='w'/>")
1383 e = doc.documentElement
1384 a1 = e.getAttributeNode("a1")
1385 a2 = e.getAttributeNode("a2")
1386 self.confirm(doc.getElementById("v") is None
1387 and not a1.isId
1388 and not a2.isId)
1389 e.setIdAttribute("a1")
1390 self.confirm(e.isSameNode(doc.getElementById("v"))
1391 and a1.isId
1392 and not a2.isId)
1393 e.setIdAttribute("a2")
1394 self.confirm(e.isSameNode(doc.getElementById("v"))
1395 and e.isSameNode(doc.getElementById("w"))
1396 and a1.isId
1397 and a2.isId)
1398 # replace the a1 node; the new node should *not* be an ID
1399 a3 = doc.createAttribute("a1")
1400 a3.value = "v"
1401 e.setAttributeNode(a3)
1402 self.confirm(doc.getElementById("v") is None
1403 and e.isSameNode(doc.getElementById("w"))
1404 and not a1.isId
1405 and a2.isId
1406 and not a3.isId)
1407 # renaming an attribute should not affect its ID-ness:
1408 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1409 self.confirm(e.isSameNode(doc.getElementById("w"))
1410 and a2.isId)
1411
1412 def testSetIdAttributeNS(self):
1413 NS1 = "http://xml.python.org/ns1"
1414 NS2 = "http://xml.python.org/ns2"
1415 doc = parseString("<doc"
1416 " xmlns:ns1='" + NS1 + "'"
1417 " xmlns:ns2='" + NS2 + "'"
1418 " ns1:a1='v' ns2:a2='w'/>")
1419 e = doc.documentElement
1420 a1 = e.getAttributeNodeNS(NS1, "a1")
1421 a2 = e.getAttributeNodeNS(NS2, "a2")
1422 self.confirm(doc.getElementById("v") is None
1423 and not a1.isId
1424 and not a2.isId)
1425 e.setIdAttributeNS(NS1, "a1")
1426 self.confirm(e.isSameNode(doc.getElementById("v"))
1427 and a1.isId
1428 and not a2.isId)
1429 e.setIdAttributeNS(NS2, "a2")
1430 self.confirm(e.isSameNode(doc.getElementById("v"))
1431 and e.isSameNode(doc.getElementById("w"))
1432 and a1.isId
1433 and a2.isId)
1434 # replace the a1 node; the new node should *not* be an ID
1435 a3 = doc.createAttributeNS(NS1, "a1")
1436 a3.value = "v"
1437 e.setAttributeNode(a3)
1438 self.confirm(e.isSameNode(doc.getElementById("w")))
1439 self.confirm(not a1.isId)
1440 self.confirm(a2.isId)
1441 self.confirm(not a3.isId)
1442 self.confirm(doc.getElementById("v") is None)
1443 # renaming an attribute should not affect its ID-ness:
1444 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1445 self.confirm(e.isSameNode(doc.getElementById("w"))
1446 and a2.isId)
1447
1448 def testSetIdAttributeNode(self):
1449 NS1 = "http://xml.python.org/ns1"
1450 NS2 = "http://xml.python.org/ns2"
1451 doc = parseString("<doc"
1452 " xmlns:ns1='" + NS1 + "'"
1453 " xmlns:ns2='" + NS2 + "'"
1454 " ns1:a1='v' ns2:a2='w'/>")
1455 e = doc.documentElement
1456 a1 = e.getAttributeNodeNS(NS1, "a1")
1457 a2 = e.getAttributeNodeNS(NS2, "a2")
1458 self.confirm(doc.getElementById("v") is None
1459 and not a1.isId
1460 and not a2.isId)
1461 e.setIdAttributeNode(a1)
1462 self.confirm(e.isSameNode(doc.getElementById("v"))
1463 and a1.isId
1464 and not a2.isId)
1465 e.setIdAttributeNode(a2)
1466 self.confirm(e.isSameNode(doc.getElementById("v"))
1467 and e.isSameNode(doc.getElementById("w"))
1468 and a1.isId
1469 and a2.isId)
1470 # replace the a1 node; the new node should *not* be an ID
1471 a3 = doc.createAttributeNS(NS1, "a1")
1472 a3.value = "v"
1473 e.setAttributeNode(a3)
1474 self.confirm(e.isSameNode(doc.getElementById("w")))
1475 self.confirm(not a1.isId)
1476 self.confirm(a2.isId)
1477 self.confirm(not a3.isId)
1478 self.confirm(doc.getElementById("v") is None)
1479 # renaming an attribute should not affect its ID-ness:
1480 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1481 self.confirm(e.isSameNode(doc.getElementById("w"))
1482 and a2.isId)
1483
1484 def testPickledDocument(self):
1485 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1486 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1487 " 'http://xml.python.org/system' [\n"
1488 " <!ELEMENT e EMPTY>\n"
1489 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1490 "]><doc attr='value'> text\n"
1491 "<?pi sample?> <!-- comment --> <e/> </doc>")
Serhiy Storchakabad12572014-12-15 14:03:42 +02001492 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
1493 s = pickle.dumps(doc, proto)
1494 doc2 = pickle.loads(s)
1495 stack = [(doc, doc2)]
1496 while stack:
1497 n1, n2 = stack.pop()
1498 self.confirm(n1.nodeType == n2.nodeType
1499 and len(n1.childNodes) == len(n2.childNodes)
1500 and n1.nodeName == n2.nodeName
1501 and not n1.isSameNode(n2)
1502 and not n2.isSameNode(n1))
1503 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1504 len(n1.entities)
1505 len(n2.entities)
1506 len(n1.notations)
1507 len(n2.notations)
1508 self.confirm(len(n1.entities) == len(n2.entities)
1509 and len(n1.notations) == len(n2.notations))
1510 for i in range(len(n1.notations)):
1511 # XXX this loop body doesn't seem to be executed?
1512 no1 = n1.notations.item(i)
1513 no2 = n1.notations.item(i)
1514 self.confirm(no1.name == no2.name
1515 and no1.publicId == no2.publicId
1516 and no1.systemId == no2.systemId)
1517 stack.append((no1, no2))
1518 for i in range(len(n1.entities)):
1519 e1 = n1.entities.item(i)
1520 e2 = n2.entities.item(i)
1521 self.confirm(e1.notationName == e2.notationName
1522 and e1.publicId == e2.publicId
1523 and e1.systemId == e2.systemId)
1524 stack.append((e1, e2))
1525 if n1.nodeType != Node.DOCUMENT_NODE:
1526 self.confirm(n1.ownerDocument.isSameNode(doc)
1527 and n2.ownerDocument.isSameNode(doc2))
1528 for i in range(len(n1.childNodes)):
1529 stack.append((n1.childNodes[i], n2.childNodes[i]))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001530
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001531 def testSerializeCommentNodeWithDoubleHyphen(self):
1532 doc = create_doc_without_doctype()
1533 doc.appendChild(doc.createComment("foo--bar"))
1534 self.assertRaises(ValueError, doc.toxml)
1535
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001536
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001537 def testEmptyXMLNSValue(self):
1538 doc = parseString("<element xmlns=''>\n"
1539 "<foo/>\n</element>")
1540 doc2 = parseString(doc.toxml())
1541 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1542
R David Murray9077d242014-04-20 00:46:05 -04001543 def testExceptionOnSpacesInXMLNSValue(self):
1544 with self.assertRaisesRegex(ValueError, 'Unsupported syntax'):
1545 parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
1546
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001547 def testDocRemoveChild(self):
1548 doc = parse(tstfile)
1549 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1550 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1551 num_children_before = len(doc.childNodes)
1552 doc.removeChild(doc.childNodes[0])
1553 num_children_after = len(doc.childNodes)
1554 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001555
Raymond Hettinger92a40552014-06-15 14:48:19 -07001556 def testProcessingInstructionNameError(self):
1557 # wrong variable in .nodeValue property will
1558 # lead to "NameError: name 'data' is not defined"
1559 doc = parse(tstfile)
1560 pi = doc.createProcessingInstruction("y", "z")
1561 pi.nodeValue = "crash"
1562
Guido van Rossumd8faa362007-04-27 19:54:29 +00001563if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -05001564 unittest.main()