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