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