blob: 70965854ed1b1ccfb87ea6e6a3baf04395268be1 [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
1155 class UserDataHandler:
1156 called = 0
1157 def handle(self, operation, key, data, src, dst):
1158 dst.setUserData(key, data + 1, self)
1159 src.setUserData(key, None, None)
1160 self.called = 1
1161
1162 def testUserData(self):
1163 dom = Document()
1164 n = dom.createElement('e')
1165 self.confirm(n.getUserData("foo") is None)
1166 n.setUserData("foo", None, None)
1167 self.confirm(n.getUserData("foo") is None)
1168 n.setUserData("foo", 12, 12)
1169 n.setUserData("bar", 13, 13)
1170 self.confirm(n.getUserData("foo") == 12)
1171 self.confirm(n.getUserData("bar") == 13)
1172 n.setUserData("foo", None, None)
1173 self.confirm(n.getUserData("foo") is None)
1174 self.confirm(n.getUserData("bar") == 13)
1175
1176 handler = self.UserDataHandler()
1177 n.setUserData("bar", 12, handler)
1178 c = n.cloneNode(1)
1179 self.confirm(handler.called
1180 and n.getUserData("bar") is None
1181 and c.getUserData("bar") == 13)
1182 n.unlink()
1183 c.unlink()
1184 dom.unlink()
1185
1186 def checkRenameNodeSharedConstraints(self, doc, node):
1187 # Make sure illegal NS usage is detected:
1188 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1189 "http://xml.python.org/ns", "xmlns:foo")
1190 doc2 = parseString("<doc/>")
1191 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1192 xml.dom.EMPTY_NAMESPACE, "foo")
1193
1194 def testRenameAttribute(self):
1195 doc = parseString("<doc a='v'/>")
1196 elem = doc.documentElement
1197 attrmap = elem.attributes
1198 attr = elem.attributes['a']
1199
1200 # Simple renaming
1201 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1202 self.confirm(attr.name == "b"
1203 and attr.nodeName == "b"
1204 and attr.localName is None
1205 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1206 and attr.prefix is None
1207 and attr.value == "v"
1208 and elem.getAttributeNode("a") is None
1209 and elem.getAttributeNode("b").isSameNode(attr)
1210 and attrmap["b"].isSameNode(attr)
1211 and attr.ownerDocument.isSameNode(doc)
1212 and attr.ownerElement.isSameNode(elem))
1213
1214 # Rename to have a namespace, no prefix
1215 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1216 self.confirm(attr.name == "c"
1217 and attr.nodeName == "c"
1218 and attr.localName == "c"
1219 and attr.namespaceURI == "http://xml.python.org/ns"
1220 and attr.prefix is None
1221 and attr.value == "v"
1222 and elem.getAttributeNode("a") is None
1223 and elem.getAttributeNode("b") is None
1224 and elem.getAttributeNode("c").isSameNode(attr)
1225 and elem.getAttributeNodeNS(
1226 "http://xml.python.org/ns", "c").isSameNode(attr)
1227 and attrmap["c"].isSameNode(attr)
1228 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1229
1230 # Rename to have a namespace, with prefix
1231 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1232 self.confirm(attr.name == "p:d"
1233 and attr.nodeName == "p:d"
1234 and attr.localName == "d"
1235 and attr.namespaceURI == "http://xml.python.org/ns2"
1236 and attr.prefix == "p"
1237 and attr.value == "v"
1238 and elem.getAttributeNode("a") is None
1239 and elem.getAttributeNode("b") is None
1240 and elem.getAttributeNode("c") is None
1241 and elem.getAttributeNodeNS(
1242 "http://xml.python.org/ns", "c") is None
1243 and elem.getAttributeNode("p:d").isSameNode(attr)
1244 and elem.getAttributeNodeNS(
1245 "http://xml.python.org/ns2", "d").isSameNode(attr)
1246 and attrmap["p:d"].isSameNode(attr)
1247 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1248
1249 # Rename back to a simple non-NS node
1250 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1251 self.confirm(attr.name == "e"
1252 and attr.nodeName == "e"
1253 and attr.localName is None
1254 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1255 and attr.prefix is None
1256 and attr.value == "v"
1257 and elem.getAttributeNode("a") is None
1258 and elem.getAttributeNode("b") is None
1259 and elem.getAttributeNode("c") is None
1260 and elem.getAttributeNode("p:d") is None
1261 and elem.getAttributeNodeNS(
1262 "http://xml.python.org/ns", "c") is None
1263 and elem.getAttributeNode("e").isSameNode(attr)
1264 and attrmap["e"].isSameNode(attr))
1265
1266 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1267 "http://xml.python.org/ns", "xmlns")
1268 self.checkRenameNodeSharedConstraints(doc, attr)
1269 doc.unlink()
1270
1271 def testRenameElement(self):
1272 doc = parseString("<doc/>")
1273 elem = doc.documentElement
1274
1275 # Simple renaming
1276 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1277 self.confirm(elem.tagName == "a"
1278 and elem.nodeName == "a"
1279 and elem.localName is None
1280 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1281 and elem.prefix is None
1282 and elem.ownerDocument.isSameNode(doc))
1283
1284 # Rename to have a namespace, no prefix
1285 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1286 self.confirm(elem.tagName == "b"
1287 and elem.nodeName == "b"
1288 and elem.localName == "b"
1289 and elem.namespaceURI == "http://xml.python.org/ns"
1290 and elem.prefix is None
1291 and elem.ownerDocument.isSameNode(doc))
1292
1293 # Rename to have a namespace, with prefix
1294 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1295 self.confirm(elem.tagName == "p:c"
1296 and elem.nodeName == "p:c"
1297 and elem.localName == "c"
1298 and elem.namespaceURI == "http://xml.python.org/ns2"
1299 and elem.prefix == "p"
1300 and elem.ownerDocument.isSameNode(doc))
1301
1302 # Rename back to a simple non-NS node
1303 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1304 self.confirm(elem.tagName == "d"
1305 and elem.nodeName == "d"
1306 and elem.localName is None
1307 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1308 and elem.prefix is None
1309 and elem.ownerDocument.isSameNode(doc))
1310
1311 self.checkRenameNodeSharedConstraints(doc, elem)
1312 doc.unlink()
1313
1314 def testRenameOther(self):
1315 # We have to create a comment node explicitly since not all DOM
1316 # builders used with minidom add comments to the DOM.
1317 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1318 xml.dom.EMPTY_NAMESPACE, "e", None)
1319 node = doc.createComment("comment")
1320 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1321 xml.dom.EMPTY_NAMESPACE, "foo")
1322 doc.unlink()
1323
1324 def testWholeText(self):
1325 doc = parseString("<doc>a</doc>")
1326 elem = doc.documentElement
1327 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001328 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001329
1330 self.checkWholeText(text, "a")
1331 elem.appendChild(doc.createTextNode("b"))
1332 self.checkWholeText(text, "ab")
1333 elem.insertBefore(doc.createCDATASection("c"), text)
1334 self.checkWholeText(text, "cab")
1335
1336 # make sure we don't cross other nodes
1337 splitter = doc.createComment("comment")
1338 elem.appendChild(splitter)
1339 text2 = doc.createTextNode("d")
1340 elem.appendChild(text2)
1341 self.checkWholeText(text, "cab")
1342 self.checkWholeText(text2, "d")
1343
1344 x = doc.createElement("x")
1345 elem.replaceChild(x, splitter)
1346 splitter = x
1347 self.checkWholeText(text, "cab")
1348 self.checkWholeText(text2, "d")
1349
1350 x = doc.createProcessingInstruction("y", "z")
1351 elem.replaceChild(x, splitter)
1352 splitter = x
1353 self.checkWholeText(text, "cab")
1354 self.checkWholeText(text2, "d")
1355
1356 elem.removeChild(splitter)
1357 self.checkWholeText(text, "cabd")
1358 self.checkWholeText(text2, "cabd")
1359
1360 def testPatch1094164(self):
1361 doc = parseString("<doc><e/></doc>")
1362 elem = doc.documentElement
1363 e = elem.firstChild
1364 self.confirm(e.parentNode is elem, "Before replaceChild()")
1365 # Check that replacing a child with itself leaves the tree unchanged
1366 elem.replaceChild(e, e)
1367 self.confirm(e.parentNode is elem, "After replaceChild()")
1368
1369 def testReplaceWholeText(self):
1370 def setup():
1371 doc = parseString("<doc>a<e/>d</doc>")
1372 elem = doc.documentElement
1373 text1 = elem.firstChild
1374 text2 = elem.lastChild
1375 splitter = text1.nextSibling
1376 elem.insertBefore(doc.createTextNode("b"), splitter)
1377 elem.insertBefore(doc.createCDATASection("c"), text1)
1378 return doc, elem, text1, splitter, text2
1379
1380 doc, elem, text1, splitter, text2 = setup()
1381 text = text1.replaceWholeText("new content")
1382 self.checkWholeText(text, "new content")
1383 self.checkWholeText(text2, "d")
1384 self.confirm(len(elem.childNodes) == 3)
1385
1386 doc, elem, text1, splitter, text2 = setup()
1387 text = text2.replaceWholeText("new content")
1388 self.checkWholeText(text, "new content")
1389 self.checkWholeText(text1, "cab")
1390 self.confirm(len(elem.childNodes) == 5)
1391
1392 doc, elem, text1, splitter, text2 = setup()
1393 text = text1.replaceWholeText("")
1394 self.checkWholeText(text2, "d")
1395 self.confirm(text is None
1396 and len(elem.childNodes) == 2)
1397
1398 def testSchemaType(self):
1399 doc = parseString(
1400 "<!DOCTYPE doc [\n"
1401 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1402 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1403 " <!ATTLIST doc id ID #IMPLIED \n"
1404 " ref IDREF #IMPLIED \n"
1405 " refs IDREFS #IMPLIED \n"
1406 " enum (a|b) #IMPLIED \n"
1407 " ent ENTITY #IMPLIED \n"
1408 " ents ENTITIES #IMPLIED \n"
1409 " nm NMTOKEN #IMPLIED \n"
1410 " nms NMTOKENS #IMPLIED \n"
1411 " text CDATA #IMPLIED \n"
1412 " >\n"
1413 "]><doc id='name' notid='name' text='splat!' enum='b'"
1414 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1415 " nm='123' nms='123 abc' />")
1416 elem = doc.documentElement
1417 # We don't want to rely on any specific loader at this point, so
1418 # just make sure we can get to all the names, and that the
1419 # DTD-based namespace is right. The names can vary by loader
1420 # since each supports a different level of DTD information.
1421 t = elem.schemaType
1422 self.confirm(t.name is None
1423 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1424 names = "id notid text enum ref refs ent ents nm nms".split()
1425 for name in names:
1426 a = elem.getAttributeNode(name)
1427 t = a.schemaType
1428 self.confirm(hasattr(t, "name")
1429 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1430
1431 def testSetIdAttribute(self):
1432 doc = parseString("<doc a1='v' a2='w'/>")
1433 e = doc.documentElement
1434 a1 = e.getAttributeNode("a1")
1435 a2 = e.getAttributeNode("a2")
1436 self.confirm(doc.getElementById("v") is None
1437 and not a1.isId
1438 and not a2.isId)
1439 e.setIdAttribute("a1")
1440 self.confirm(e.isSameNode(doc.getElementById("v"))
1441 and a1.isId
1442 and not a2.isId)
1443 e.setIdAttribute("a2")
1444 self.confirm(e.isSameNode(doc.getElementById("v"))
1445 and e.isSameNode(doc.getElementById("w"))
1446 and a1.isId
1447 and a2.isId)
1448 # replace the a1 node; the new node should *not* be an ID
1449 a3 = doc.createAttribute("a1")
1450 a3.value = "v"
1451 e.setAttributeNode(a3)
1452 self.confirm(doc.getElementById("v") is None
1453 and e.isSameNode(doc.getElementById("w"))
1454 and not a1.isId
1455 and a2.isId
1456 and not a3.isId)
1457 # renaming an attribute should not affect its ID-ness:
1458 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1459 self.confirm(e.isSameNode(doc.getElementById("w"))
1460 and a2.isId)
1461
1462 def testSetIdAttributeNS(self):
1463 NS1 = "http://xml.python.org/ns1"
1464 NS2 = "http://xml.python.org/ns2"
1465 doc = parseString("<doc"
1466 " xmlns:ns1='" + NS1 + "'"
1467 " xmlns:ns2='" + NS2 + "'"
1468 " ns1:a1='v' ns2:a2='w'/>")
1469 e = doc.documentElement
1470 a1 = e.getAttributeNodeNS(NS1, "a1")
1471 a2 = e.getAttributeNodeNS(NS2, "a2")
1472 self.confirm(doc.getElementById("v") is None
1473 and not a1.isId
1474 and not a2.isId)
1475 e.setIdAttributeNS(NS1, "a1")
1476 self.confirm(e.isSameNode(doc.getElementById("v"))
1477 and a1.isId
1478 and not a2.isId)
1479 e.setIdAttributeNS(NS2, "a2")
1480 self.confirm(e.isSameNode(doc.getElementById("v"))
1481 and e.isSameNode(doc.getElementById("w"))
1482 and a1.isId
1483 and a2.isId)
1484 # replace the a1 node; the new node should *not* be an ID
1485 a3 = doc.createAttributeNS(NS1, "a1")
1486 a3.value = "v"
1487 e.setAttributeNode(a3)
1488 self.confirm(e.isSameNode(doc.getElementById("w")))
1489 self.confirm(not a1.isId)
1490 self.confirm(a2.isId)
1491 self.confirm(not a3.isId)
1492 self.confirm(doc.getElementById("v") is None)
1493 # renaming an attribute should not affect its ID-ness:
1494 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1495 self.confirm(e.isSameNode(doc.getElementById("w"))
1496 and a2.isId)
1497
1498 def testSetIdAttributeNode(self):
1499 NS1 = "http://xml.python.org/ns1"
1500 NS2 = "http://xml.python.org/ns2"
1501 doc = parseString("<doc"
1502 " xmlns:ns1='" + NS1 + "'"
1503 " xmlns:ns2='" + NS2 + "'"
1504 " ns1:a1='v' ns2:a2='w'/>")
1505 e = doc.documentElement
1506 a1 = e.getAttributeNodeNS(NS1, "a1")
1507 a2 = e.getAttributeNodeNS(NS2, "a2")
1508 self.confirm(doc.getElementById("v") is None
1509 and not a1.isId
1510 and not a2.isId)
1511 e.setIdAttributeNode(a1)
1512 self.confirm(e.isSameNode(doc.getElementById("v"))
1513 and a1.isId
1514 and not a2.isId)
1515 e.setIdAttributeNode(a2)
1516 self.confirm(e.isSameNode(doc.getElementById("v"))
1517 and e.isSameNode(doc.getElementById("w"))
1518 and a1.isId
1519 and a2.isId)
1520 # replace the a1 node; the new node should *not* be an ID
1521 a3 = doc.createAttributeNS(NS1, "a1")
1522 a3.value = "v"
1523 e.setAttributeNode(a3)
1524 self.confirm(e.isSameNode(doc.getElementById("w")))
1525 self.confirm(not a1.isId)
1526 self.confirm(a2.isId)
1527 self.confirm(not a3.isId)
1528 self.confirm(doc.getElementById("v") is None)
1529 # renaming an attribute should not affect its ID-ness:
1530 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1531 self.confirm(e.isSameNode(doc.getElementById("w"))
1532 and a2.isId)
1533
Serhiy Storchakac4722462015-11-26 23:49:42 +02001534 def assert_recursive_equal(self, doc, doc2):
1535 stack = [(doc, doc2)]
1536 while stack:
1537 n1, n2 = stack.pop()
1538 self.assertEqual(n1.nodeType, n2.nodeType)
1539 self.assertEqual(len(n1.childNodes), len(n2.childNodes))
1540 self.assertEqual(n1.nodeName, n2.nodeName)
1541 self.assertFalse(n1.isSameNode(n2))
1542 self.assertFalse(n2.isSameNode(n1))
1543 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1544 len(n1.entities)
1545 len(n2.entities)
1546 len(n1.notations)
1547 len(n2.notations)
1548 self.assertEqual(len(n1.entities), len(n2.entities))
1549 self.assertEqual(len(n1.notations), len(n2.notations))
1550 for i in range(len(n1.notations)):
1551 # XXX this loop body doesn't seem to be executed?
1552 no1 = n1.notations.item(i)
1553 no2 = n1.notations.item(i)
1554 self.assertEqual(no1.name, no2.name)
1555 self.assertEqual(no1.publicId, no2.publicId)
1556 self.assertEqual(no1.systemId, no2.systemId)
1557 stack.append((no1, no2))
1558 for i in range(len(n1.entities)):
1559 e1 = n1.entities.item(i)
1560 e2 = n2.entities.item(i)
1561 self.assertEqual(e1.notationName, e2.notationName)
1562 self.assertEqual(e1.publicId, e2.publicId)
1563 self.assertEqual(e1.systemId, e2.systemId)
1564 stack.append((e1, e2))
1565 if n1.nodeType != Node.DOCUMENT_NODE:
1566 self.assertTrue(n1.ownerDocument.isSameNode(doc))
1567 self.assertTrue(n2.ownerDocument.isSameNode(doc2))
1568 for i in range(len(n1.childNodes)):
1569 stack.append((n1.childNodes[i], n2.childNodes[i]))
1570
Guido van Rossumd8faa362007-04-27 19:54:29 +00001571 def testPickledDocument(self):
Serhiy Storchakac4722462015-11-26 23:49:42 +02001572 doc = parseString(sample)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001573 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
1574 s = pickle.dumps(doc, proto)
1575 doc2 = pickle.loads(s)
Serhiy Storchakac4722462015-11-26 23:49:42 +02001576 self.assert_recursive_equal(doc, doc2)
1577
1578 def testDeepcopiedDocument(self):
1579 doc = parseString(sample)
1580 doc2 = copy.deepcopy(doc)
1581 self.assert_recursive_equal(doc, doc2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001582
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001583 def testSerializeCommentNodeWithDoubleHyphen(self):
1584 doc = create_doc_without_doctype()
1585 doc.appendChild(doc.createComment("foo--bar"))
1586 self.assertRaises(ValueError, doc.toxml)
1587
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001588
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001589 def testEmptyXMLNSValue(self):
1590 doc = parseString("<element xmlns=''>\n"
1591 "<foo/>\n</element>")
1592 doc2 = parseString(doc.toxml())
1593 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1594
R David Murray9077d242014-04-20 00:46:05 -04001595 def testExceptionOnSpacesInXMLNSValue(self):
1596 with self.assertRaisesRegex(ValueError, 'Unsupported syntax'):
1597 parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
1598
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001599 def testDocRemoveChild(self):
1600 doc = parse(tstfile)
1601 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1602 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1603 num_children_before = len(doc.childNodes)
1604 doc.removeChild(doc.childNodes[0])
1605 num_children_after = len(doc.childNodes)
1606 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001607
Raymond Hettinger92a40552014-06-15 14:48:19 -07001608 def testProcessingInstructionNameError(self):
1609 # wrong variable in .nodeValue property will
1610 # lead to "NameError: name 'data' is not defined"
1611 doc = parse(tstfile)
1612 pi = doc.createProcessingInstruction("y", "z")
1613 pi.nodeValue = "crash"
1614
Diego Rojas5598cc92018-11-07 09:09:04 -05001615 def test_minidom_attribute_order(self):
1616 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
1617 doc = parseString(xml_str)
1618 output = io.StringIO()
1619 doc.writexml(output)
1620 self.assertEqual(output.getvalue(), xml_str)
1621
1622 def test_toxml_with_attributes_ordered(self):
1623 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
1624 doc = parseString(xml_str)
1625 self.assertEqual(doc.toxml(), xml_str)
1626
1627 def test_toprettyxml_with_attributes_ordered(self):
1628 xml_str = '<?xml version="1.0" ?><curriculum status="public" company="example"/>'
1629 doc = parseString(xml_str)
1630 self.assertEqual(doc.toprettyxml(),
1631 '<?xml version="1.0" ?>\n'
1632 '<curriculum status="public" company="example"/>\n')
1633
Vladimir Surjaninov384b81d2019-03-27 08:58:49 +03001634 def test_toprettyxml_with_cdata(self):
1635 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>'
1636 doc = parseString(xml_str)
1637 self.assertEqual(doc.toprettyxml(),
1638 '<?xml version="1.0" ?>\n'
1639 '<root>\n'
1640 '\t<node><![CDATA[</data>]]></node>\n'
1641 '</root>\n')
1642
1643 def test_cdata_parsing(self):
1644 xml_str = '<?xml version="1.0" ?><root><node><![CDATA[</data>]]></node></root>'
1645 dom1 = parseString(xml_str)
1646 self.checkWholeText(dom1.getElementsByTagName('node')[0].firstChild, '</data>')
1647 dom2 = parseString(dom1.toprettyxml())
1648 self.checkWholeText(dom2.getElementsByTagName('node')[0].firstChild, '</data>')
1649
Guido van Rossumd8faa362007-04-27 19:54:29 +00001650if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -05001651 unittest.main()