blob: e91cdba1eb2eff0c20e50e9806a1ff74671dcb29 [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
Zachary Ware38c707e2015-04-13 15:00:43 -05005from test.support import findfile
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
Florent Xiclunaf15351d2010-03-13 23:24:31 +000014tstfile = 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)
arikrupnik5bfa0582018-06-06 23:42:38 -0500328 self.assertIs(node, child.removeAttributeNode(node))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000329 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
839 def testNormalize(self):
840 doc = parseString("<doc/>")
841 root = doc.documentElement
842 root.appendChild(doc.createTextNode("first"))
843 root.appendChild(doc.createTextNode("second"))
844 self.confirm(len(root.childNodes) == 2
845 and root.childNodes.length == 2,
846 "testNormalize -- preparation")
847 doc.normalize()
848 self.confirm(len(root.childNodes) == 1
849 and root.childNodes.length == 1
850 and root.firstChild is root.lastChild
851 and root.firstChild.data == "firstsecond"
852 , "testNormalize -- result")
853 doc.unlink()
854
855 doc = parseString("<doc/>")
856 root = doc.documentElement
857 root.appendChild(doc.createTextNode(""))
858 doc.normalize()
859 self.confirm(len(root.childNodes) == 0
860 and root.childNodes.length == 0,
861 "testNormalize -- single empty node removed")
862 doc.unlink()
863
R. David Murraydc6da8a2009-04-09 22:16:43 +0000864 def testNormalizeCombineAndNextSibling(self):
865 doc = parseString("<doc/>")
866 root = doc.documentElement
867 root.appendChild(doc.createTextNode("first"))
868 root.appendChild(doc.createTextNode("second"))
869 root.appendChild(doc.createElement("i"))
870 self.confirm(len(root.childNodes) == 3
871 and root.childNodes.length == 3,
872 "testNormalizeCombineAndNextSibling -- preparation")
873 doc.normalize()
874 self.confirm(len(root.childNodes) == 2
875 and root.childNodes.length == 2
876 and root.firstChild.data == "firstsecond"
877 and root.firstChild is not root.lastChild
878 and root.firstChild.nextSibling is root.lastChild
879 and root.firstChild.previousSibling is None
880 and root.lastChild.previousSibling is root.firstChild
881 and root.lastChild.nextSibling is None
882 , "testNormalizeCombinedAndNextSibling -- result")
883 doc.unlink()
884
885 def testNormalizeDeleteWithPrevSibling(self):
886 doc = parseString("<doc/>")
887 root = doc.documentElement
888 root.appendChild(doc.createTextNode("first"))
889 root.appendChild(doc.createTextNode(""))
890 self.confirm(len(root.childNodes) == 2
891 and root.childNodes.length == 2,
892 "testNormalizeDeleteWithPrevSibling -- preparation")
893 doc.normalize()
894 self.confirm(len(root.childNodes) == 1
895 and root.childNodes.length == 1
896 and root.firstChild.data == "first"
897 and root.firstChild is root.lastChild
898 and root.firstChild.nextSibling is None
899 and root.firstChild.previousSibling is None
900 , "testNormalizeDeleteWithPrevSibling -- result")
901 doc.unlink()
902
903 def testNormalizeDeleteWithNextSibling(self):
904 doc = parseString("<doc/>")
905 root = doc.documentElement
906 root.appendChild(doc.createTextNode(""))
907 root.appendChild(doc.createTextNode("second"))
908 self.confirm(len(root.childNodes) == 2
909 and root.childNodes.length == 2,
910 "testNormalizeDeleteWithNextSibling -- preparation")
911 doc.normalize()
912 self.confirm(len(root.childNodes) == 1
913 and root.childNodes.length == 1
914 and root.firstChild.data == "second"
915 and root.firstChild is root.lastChild
916 and root.firstChild.nextSibling is None
917 and root.firstChild.previousSibling is None
918 , "testNormalizeDeleteWithNextSibling -- result")
919 doc.unlink()
920
921 def testNormalizeDeleteWithTwoNonTextSiblings(self):
922 doc = parseString("<doc/>")
923 root = doc.documentElement
924 root.appendChild(doc.createElement("i"))
925 root.appendChild(doc.createTextNode(""))
926 root.appendChild(doc.createElement("i"))
927 self.confirm(len(root.childNodes) == 3
928 and root.childNodes.length == 3,
929 "testNormalizeDeleteWithTwoSiblings -- preparation")
930 doc.normalize()
931 self.confirm(len(root.childNodes) == 2
932 and root.childNodes.length == 2
933 and root.firstChild is not root.lastChild
934 and root.firstChild.nextSibling is root.lastChild
935 and root.firstChild.previousSibling is None
936 and root.lastChild.previousSibling is root.firstChild
937 and root.lastChild.nextSibling is None
938 , "testNormalizeDeleteWithTwoSiblings -- result")
939 doc.unlink()
940
941 def testNormalizeDeleteAndCombine(self):
942 doc = parseString("<doc/>")
943 root = doc.documentElement
944 root.appendChild(doc.createTextNode(""))
945 root.appendChild(doc.createTextNode("second"))
946 root.appendChild(doc.createTextNode(""))
947 root.appendChild(doc.createTextNode("fourth"))
948 root.appendChild(doc.createTextNode(""))
949 self.confirm(len(root.childNodes) == 5
950 and root.childNodes.length == 5,
951 "testNormalizeDeleteAndCombine -- preparation")
952 doc.normalize()
953 self.confirm(len(root.childNodes) == 1
954 and root.childNodes.length == 1
955 and root.firstChild is root.lastChild
956 and root.firstChild.data == "secondfourth"
957 and root.firstChild.previousSibling is None
958 and root.firstChild.nextSibling is None
959 , "testNormalizeDeleteAndCombine -- result")
960 doc.unlink()
961
962 def testNormalizeRecursion(self):
963 doc = parseString("<doc>"
964 "<o>"
965 "<i/>"
966 "t"
967 #
968 #x
969 "</o>"
970 "<o>"
971 "<o>"
972 "t2"
973 #x2
974 "</o>"
975 "t3"
976 #x3
977 "</o>"
978 #
979 "</doc>")
980 root = doc.documentElement
981 root.childNodes[0].appendChild(doc.createTextNode(""))
982 root.childNodes[0].appendChild(doc.createTextNode("x"))
983 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
984 root.childNodes[1].appendChild(doc.createTextNode("x3"))
985 root.appendChild(doc.createTextNode(""))
986 self.confirm(len(root.childNodes) == 3
987 and root.childNodes.length == 3
988 and len(root.childNodes[0].childNodes) == 4
989 and root.childNodes[0].childNodes.length == 4
990 and len(root.childNodes[1].childNodes) == 3
991 and root.childNodes[1].childNodes.length == 3
992 and len(root.childNodes[1].childNodes[0].childNodes) == 2
993 and root.childNodes[1].childNodes[0].childNodes.length == 2
994 , "testNormalize2 -- preparation")
995 doc.normalize()
996 self.confirm(len(root.childNodes) == 2
997 and root.childNodes.length == 2
998 and len(root.childNodes[0].childNodes) == 2
999 and root.childNodes[0].childNodes.length == 2
1000 and len(root.childNodes[1].childNodes) == 2
1001 and root.childNodes[1].childNodes.length == 2
1002 and len(root.childNodes[1].childNodes[0].childNodes) == 1
1003 and root.childNodes[1].childNodes[0].childNodes.length == 1
1004 , "testNormalize2 -- childNodes lengths")
1005 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
1006 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
1007 and root.childNodes[1].childNodes[1].data == "t3x3"
1008 , "testNormalize2 -- joined text fields")
1009 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
1010 and root.childNodes[0].childNodes[1].previousSibling
1011 is root.childNodes[0].childNodes[0]
1012 and root.childNodes[0].childNodes[0].previousSibling is None
1013 and root.childNodes[0].childNodes[0].nextSibling
1014 is root.childNodes[0].childNodes[1]
1015 and root.childNodes[1].childNodes[1].nextSibling is None
1016 and root.childNodes[1].childNodes[1].previousSibling
1017 is root.childNodes[1].childNodes[0]
1018 and root.childNodes[1].childNodes[0].previousSibling is None
1019 and root.childNodes[1].childNodes[0].nextSibling
1020 is root.childNodes[1].childNodes[1]
1021 , "testNormalize2 -- sibling pointers")
1022 doc.unlink()
1023
1024
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001025 def testBug0777884(self):
1026 doc = parseString("<o>text</o>")
1027 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001028 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001029 # Should run quietly, doing nothing.
1030 text.normalize()
1031 doc.unlink()
1032
Christian Heimes05e8be12008-02-23 18:30:17 +00001033 def testBug1433694(self):
1034 doc = parseString("<o><i/>t</o>")
1035 node = doc.documentElement
1036 node.childNodes[1].nodeValue = ""
1037 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +00001038 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +00001039 "Final child's .nextSibling should be None")
1040
Guido van Rossumd8faa362007-04-27 19:54:29 +00001041 def testSiblings(self):
1042 doc = parseString("<doc><?pi?>text?<elm/></doc>")
1043 root = doc.documentElement
1044 (pi, text, elm) = root.childNodes
1045
1046 self.confirm(pi.nextSibling is text and
1047 pi.previousSibling is None and
1048 text.nextSibling is elm and
1049 text.previousSibling is pi and
1050 elm.nextSibling is None and
1051 elm.previousSibling is text, "testSiblings")
1052
1053 doc.unlink()
1054
1055 def testParents(self):
1056 doc = parseString(
1057 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
1058 root = doc.documentElement
1059 elm1 = root.childNodes[0]
1060 (elm2a, elm2b) = elm1.childNodes
1061 elm3 = elm2b.childNodes[0]
1062
1063 self.confirm(root.parentNode is doc and
1064 elm1.parentNode is root and
1065 elm2a.parentNode is elm1 and
1066 elm2b.parentNode is elm1 and
1067 elm3.parentNode is elm2b, "testParents")
1068 doc.unlink()
1069
1070 def testNodeListItem(self):
1071 doc = parseString("<doc><e/><e/></doc>")
1072 children = doc.childNodes
1073 docelem = children[0]
1074 self.confirm(children[0] is children.item(0)
1075 and children.item(1) is None
1076 and docelem.childNodes.item(0) is docelem.childNodes[0]
1077 and docelem.childNodes.item(1) is docelem.childNodes[1]
1078 and docelem.childNodes.item(0).childNodes.item(0) is None,
1079 "test NodeList.item()")
1080 doc.unlink()
1081
Guido van Rossumd8faa362007-04-27 19:54:29 +00001082 def testEncodings(self):
1083 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001084 self.assertEqual(doc.toxml(),
1085 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1086 self.assertEqual(doc.toxml('utf-8'),
1087 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1088 self.assertEqual(doc.toxml('iso-8859-15'),
1089 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Eli Bendersky8a805022012-07-13 09:52:39 +03001090 self.assertEqual(doc.toxml('us-ascii'),
1091 b'<?xml version="1.0" encoding="us-ascii"?><foo>&#8364;</foo>')
1092 self.assertEqual(doc.toxml('utf-16'),
1093 '<?xml version="1.0" encoding="utf-16"?>'
1094 '<foo>\u20ac</foo>'.encode('utf-16'))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001095
Andrew Svetlov737fb892012-12-18 21:14:22 +02001096 # Verify that character decoding errors raise exceptions instead
Guido van Rossumd8faa362007-04-27 19:54:29 +00001097 # of crashing
1098 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001099 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001100
1101 doc.unlink()
1102
1103 class UserDataHandler:
1104 called = 0
1105 def handle(self, operation, key, data, src, dst):
1106 dst.setUserData(key, data + 1, self)
1107 src.setUserData(key, None, None)
1108 self.called = 1
1109
1110 def testUserData(self):
1111 dom = Document()
1112 n = dom.createElement('e')
1113 self.confirm(n.getUserData("foo") is None)
1114 n.setUserData("foo", None, None)
1115 self.confirm(n.getUserData("foo") is None)
1116 n.setUserData("foo", 12, 12)
1117 n.setUserData("bar", 13, 13)
1118 self.confirm(n.getUserData("foo") == 12)
1119 self.confirm(n.getUserData("bar") == 13)
1120 n.setUserData("foo", None, None)
1121 self.confirm(n.getUserData("foo") is None)
1122 self.confirm(n.getUserData("bar") == 13)
1123
1124 handler = self.UserDataHandler()
1125 n.setUserData("bar", 12, handler)
1126 c = n.cloneNode(1)
1127 self.confirm(handler.called
1128 and n.getUserData("bar") is None
1129 and c.getUserData("bar") == 13)
1130 n.unlink()
1131 c.unlink()
1132 dom.unlink()
1133
1134 def checkRenameNodeSharedConstraints(self, doc, node):
1135 # Make sure illegal NS usage is detected:
1136 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1137 "http://xml.python.org/ns", "xmlns:foo")
1138 doc2 = parseString("<doc/>")
1139 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1140 xml.dom.EMPTY_NAMESPACE, "foo")
1141
1142 def testRenameAttribute(self):
1143 doc = parseString("<doc a='v'/>")
1144 elem = doc.documentElement
1145 attrmap = elem.attributes
1146 attr = elem.attributes['a']
1147
1148 # Simple renaming
1149 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1150 self.confirm(attr.name == "b"
1151 and attr.nodeName == "b"
1152 and attr.localName is None
1153 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1154 and attr.prefix is None
1155 and attr.value == "v"
1156 and elem.getAttributeNode("a") is None
1157 and elem.getAttributeNode("b").isSameNode(attr)
1158 and attrmap["b"].isSameNode(attr)
1159 and attr.ownerDocument.isSameNode(doc)
1160 and attr.ownerElement.isSameNode(elem))
1161
1162 # Rename to have a namespace, no prefix
1163 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1164 self.confirm(attr.name == "c"
1165 and attr.nodeName == "c"
1166 and attr.localName == "c"
1167 and attr.namespaceURI == "http://xml.python.org/ns"
1168 and attr.prefix is None
1169 and attr.value == "v"
1170 and elem.getAttributeNode("a") is None
1171 and elem.getAttributeNode("b") is None
1172 and elem.getAttributeNode("c").isSameNode(attr)
1173 and elem.getAttributeNodeNS(
1174 "http://xml.python.org/ns", "c").isSameNode(attr)
1175 and attrmap["c"].isSameNode(attr)
1176 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1177
1178 # Rename to have a namespace, with prefix
1179 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1180 self.confirm(attr.name == "p:d"
1181 and attr.nodeName == "p:d"
1182 and attr.localName == "d"
1183 and attr.namespaceURI == "http://xml.python.org/ns2"
1184 and attr.prefix == "p"
1185 and attr.value == "v"
1186 and elem.getAttributeNode("a") is None
1187 and elem.getAttributeNode("b") is None
1188 and elem.getAttributeNode("c") is None
1189 and elem.getAttributeNodeNS(
1190 "http://xml.python.org/ns", "c") is None
1191 and elem.getAttributeNode("p:d").isSameNode(attr)
1192 and elem.getAttributeNodeNS(
1193 "http://xml.python.org/ns2", "d").isSameNode(attr)
1194 and attrmap["p:d"].isSameNode(attr)
1195 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1196
1197 # Rename back to a simple non-NS node
1198 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1199 self.confirm(attr.name == "e"
1200 and attr.nodeName == "e"
1201 and attr.localName is None
1202 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1203 and attr.prefix is None
1204 and attr.value == "v"
1205 and elem.getAttributeNode("a") is None
1206 and elem.getAttributeNode("b") is None
1207 and elem.getAttributeNode("c") is None
1208 and elem.getAttributeNode("p:d") is None
1209 and elem.getAttributeNodeNS(
1210 "http://xml.python.org/ns", "c") is None
1211 and elem.getAttributeNode("e").isSameNode(attr)
1212 and attrmap["e"].isSameNode(attr))
1213
1214 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1215 "http://xml.python.org/ns", "xmlns")
1216 self.checkRenameNodeSharedConstraints(doc, attr)
1217 doc.unlink()
1218
1219 def testRenameElement(self):
1220 doc = parseString("<doc/>")
1221 elem = doc.documentElement
1222
1223 # Simple renaming
1224 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1225 self.confirm(elem.tagName == "a"
1226 and elem.nodeName == "a"
1227 and elem.localName is None
1228 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1229 and elem.prefix is None
1230 and elem.ownerDocument.isSameNode(doc))
1231
1232 # Rename to have a namespace, no prefix
1233 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1234 self.confirm(elem.tagName == "b"
1235 and elem.nodeName == "b"
1236 and elem.localName == "b"
1237 and elem.namespaceURI == "http://xml.python.org/ns"
1238 and elem.prefix is None
1239 and elem.ownerDocument.isSameNode(doc))
1240
1241 # Rename to have a namespace, with prefix
1242 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1243 self.confirm(elem.tagName == "p:c"
1244 and elem.nodeName == "p:c"
1245 and elem.localName == "c"
1246 and elem.namespaceURI == "http://xml.python.org/ns2"
1247 and elem.prefix == "p"
1248 and elem.ownerDocument.isSameNode(doc))
1249
1250 # Rename back to a simple non-NS node
1251 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1252 self.confirm(elem.tagName == "d"
1253 and elem.nodeName == "d"
1254 and elem.localName is None
1255 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1256 and elem.prefix is None
1257 and elem.ownerDocument.isSameNode(doc))
1258
1259 self.checkRenameNodeSharedConstraints(doc, elem)
1260 doc.unlink()
1261
1262 def testRenameOther(self):
1263 # We have to create a comment node explicitly since not all DOM
1264 # builders used with minidom add comments to the DOM.
1265 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1266 xml.dom.EMPTY_NAMESPACE, "e", None)
1267 node = doc.createComment("comment")
1268 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1269 xml.dom.EMPTY_NAMESPACE, "foo")
1270 doc.unlink()
1271
1272 def testWholeText(self):
1273 doc = parseString("<doc>a</doc>")
1274 elem = doc.documentElement
1275 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001276 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001277
1278 self.checkWholeText(text, "a")
1279 elem.appendChild(doc.createTextNode("b"))
1280 self.checkWholeText(text, "ab")
1281 elem.insertBefore(doc.createCDATASection("c"), text)
1282 self.checkWholeText(text, "cab")
1283
1284 # make sure we don't cross other nodes
1285 splitter = doc.createComment("comment")
1286 elem.appendChild(splitter)
1287 text2 = doc.createTextNode("d")
1288 elem.appendChild(text2)
1289 self.checkWholeText(text, "cab")
1290 self.checkWholeText(text2, "d")
1291
1292 x = doc.createElement("x")
1293 elem.replaceChild(x, splitter)
1294 splitter = x
1295 self.checkWholeText(text, "cab")
1296 self.checkWholeText(text2, "d")
1297
1298 x = doc.createProcessingInstruction("y", "z")
1299 elem.replaceChild(x, splitter)
1300 splitter = x
1301 self.checkWholeText(text, "cab")
1302 self.checkWholeText(text2, "d")
1303
1304 elem.removeChild(splitter)
1305 self.checkWholeText(text, "cabd")
1306 self.checkWholeText(text2, "cabd")
1307
1308 def testPatch1094164(self):
1309 doc = parseString("<doc><e/></doc>")
1310 elem = doc.documentElement
1311 e = elem.firstChild
1312 self.confirm(e.parentNode is elem, "Before replaceChild()")
1313 # Check that replacing a child with itself leaves the tree unchanged
1314 elem.replaceChild(e, e)
1315 self.confirm(e.parentNode is elem, "After replaceChild()")
1316
1317 def testReplaceWholeText(self):
1318 def setup():
1319 doc = parseString("<doc>a<e/>d</doc>")
1320 elem = doc.documentElement
1321 text1 = elem.firstChild
1322 text2 = elem.lastChild
1323 splitter = text1.nextSibling
1324 elem.insertBefore(doc.createTextNode("b"), splitter)
1325 elem.insertBefore(doc.createCDATASection("c"), text1)
1326 return doc, elem, text1, splitter, text2
1327
1328 doc, elem, text1, splitter, text2 = setup()
1329 text = text1.replaceWholeText("new content")
1330 self.checkWholeText(text, "new content")
1331 self.checkWholeText(text2, "d")
1332 self.confirm(len(elem.childNodes) == 3)
1333
1334 doc, elem, text1, splitter, text2 = setup()
1335 text = text2.replaceWholeText("new content")
1336 self.checkWholeText(text, "new content")
1337 self.checkWholeText(text1, "cab")
1338 self.confirm(len(elem.childNodes) == 5)
1339
1340 doc, elem, text1, splitter, text2 = setup()
1341 text = text1.replaceWholeText("")
1342 self.checkWholeText(text2, "d")
1343 self.confirm(text is None
1344 and len(elem.childNodes) == 2)
1345
1346 def testSchemaType(self):
1347 doc = parseString(
1348 "<!DOCTYPE doc [\n"
1349 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1350 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1351 " <!ATTLIST doc id ID #IMPLIED \n"
1352 " ref IDREF #IMPLIED \n"
1353 " refs IDREFS #IMPLIED \n"
1354 " enum (a|b) #IMPLIED \n"
1355 " ent ENTITY #IMPLIED \n"
1356 " ents ENTITIES #IMPLIED \n"
1357 " nm NMTOKEN #IMPLIED \n"
1358 " nms NMTOKENS #IMPLIED \n"
1359 " text CDATA #IMPLIED \n"
1360 " >\n"
1361 "]><doc id='name' notid='name' text='splat!' enum='b'"
1362 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1363 " nm='123' nms='123 abc' />")
1364 elem = doc.documentElement
1365 # We don't want to rely on any specific loader at this point, so
1366 # just make sure we can get to all the names, and that the
1367 # DTD-based namespace is right. The names can vary by loader
1368 # since each supports a different level of DTD information.
1369 t = elem.schemaType
1370 self.confirm(t.name is None
1371 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1372 names = "id notid text enum ref refs ent ents nm nms".split()
1373 for name in names:
1374 a = elem.getAttributeNode(name)
1375 t = a.schemaType
1376 self.confirm(hasattr(t, "name")
1377 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1378
1379 def testSetIdAttribute(self):
1380 doc = parseString("<doc a1='v' a2='w'/>")
1381 e = doc.documentElement
1382 a1 = e.getAttributeNode("a1")
1383 a2 = e.getAttributeNode("a2")
1384 self.confirm(doc.getElementById("v") is None
1385 and not a1.isId
1386 and not a2.isId)
1387 e.setIdAttribute("a1")
1388 self.confirm(e.isSameNode(doc.getElementById("v"))
1389 and a1.isId
1390 and not a2.isId)
1391 e.setIdAttribute("a2")
1392 self.confirm(e.isSameNode(doc.getElementById("v"))
1393 and e.isSameNode(doc.getElementById("w"))
1394 and a1.isId
1395 and a2.isId)
1396 # replace the a1 node; the new node should *not* be an ID
1397 a3 = doc.createAttribute("a1")
1398 a3.value = "v"
1399 e.setAttributeNode(a3)
1400 self.confirm(doc.getElementById("v") is None
1401 and e.isSameNode(doc.getElementById("w"))
1402 and not a1.isId
1403 and a2.isId
1404 and not a3.isId)
1405 # renaming an attribute should not affect its ID-ness:
1406 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1407 self.confirm(e.isSameNode(doc.getElementById("w"))
1408 and a2.isId)
1409
1410 def testSetIdAttributeNS(self):
1411 NS1 = "http://xml.python.org/ns1"
1412 NS2 = "http://xml.python.org/ns2"
1413 doc = parseString("<doc"
1414 " xmlns:ns1='" + NS1 + "'"
1415 " xmlns:ns2='" + NS2 + "'"
1416 " ns1:a1='v' ns2:a2='w'/>")
1417 e = doc.documentElement
1418 a1 = e.getAttributeNodeNS(NS1, "a1")
1419 a2 = e.getAttributeNodeNS(NS2, "a2")
1420 self.confirm(doc.getElementById("v") is None
1421 and not a1.isId
1422 and not a2.isId)
1423 e.setIdAttributeNS(NS1, "a1")
1424 self.confirm(e.isSameNode(doc.getElementById("v"))
1425 and a1.isId
1426 and not a2.isId)
1427 e.setIdAttributeNS(NS2, "a2")
1428 self.confirm(e.isSameNode(doc.getElementById("v"))
1429 and e.isSameNode(doc.getElementById("w"))
1430 and a1.isId
1431 and a2.isId)
1432 # replace the a1 node; the new node should *not* be an ID
1433 a3 = doc.createAttributeNS(NS1, "a1")
1434 a3.value = "v"
1435 e.setAttributeNode(a3)
1436 self.confirm(e.isSameNode(doc.getElementById("w")))
1437 self.confirm(not a1.isId)
1438 self.confirm(a2.isId)
1439 self.confirm(not a3.isId)
1440 self.confirm(doc.getElementById("v") is None)
1441 # renaming an attribute should not affect its ID-ness:
1442 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1443 self.confirm(e.isSameNode(doc.getElementById("w"))
1444 and a2.isId)
1445
1446 def testSetIdAttributeNode(self):
1447 NS1 = "http://xml.python.org/ns1"
1448 NS2 = "http://xml.python.org/ns2"
1449 doc = parseString("<doc"
1450 " xmlns:ns1='" + NS1 + "'"
1451 " xmlns:ns2='" + NS2 + "'"
1452 " ns1:a1='v' ns2:a2='w'/>")
1453 e = doc.documentElement
1454 a1 = e.getAttributeNodeNS(NS1, "a1")
1455 a2 = e.getAttributeNodeNS(NS2, "a2")
1456 self.confirm(doc.getElementById("v") is None
1457 and not a1.isId
1458 and not a2.isId)
1459 e.setIdAttributeNode(a1)
1460 self.confirm(e.isSameNode(doc.getElementById("v"))
1461 and a1.isId
1462 and not a2.isId)
1463 e.setIdAttributeNode(a2)
1464 self.confirm(e.isSameNode(doc.getElementById("v"))
1465 and e.isSameNode(doc.getElementById("w"))
1466 and a1.isId
1467 and a2.isId)
1468 # replace the a1 node; the new node should *not* be an ID
1469 a3 = doc.createAttributeNS(NS1, "a1")
1470 a3.value = "v"
1471 e.setAttributeNode(a3)
1472 self.confirm(e.isSameNode(doc.getElementById("w")))
1473 self.confirm(not a1.isId)
1474 self.confirm(a2.isId)
1475 self.confirm(not a3.isId)
1476 self.confirm(doc.getElementById("v") is None)
1477 # renaming an attribute should not affect its ID-ness:
1478 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1479 self.confirm(e.isSameNode(doc.getElementById("w"))
1480 and a2.isId)
1481
Serhiy Storchakac4722462015-11-26 23:49:42 +02001482 def assert_recursive_equal(self, doc, doc2):
1483 stack = [(doc, doc2)]
1484 while stack:
1485 n1, n2 = stack.pop()
1486 self.assertEqual(n1.nodeType, n2.nodeType)
1487 self.assertEqual(len(n1.childNodes), len(n2.childNodes))
1488 self.assertEqual(n1.nodeName, n2.nodeName)
1489 self.assertFalse(n1.isSameNode(n2))
1490 self.assertFalse(n2.isSameNode(n1))
1491 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1492 len(n1.entities)
1493 len(n2.entities)
1494 len(n1.notations)
1495 len(n2.notations)
1496 self.assertEqual(len(n1.entities), len(n2.entities))
1497 self.assertEqual(len(n1.notations), len(n2.notations))
1498 for i in range(len(n1.notations)):
1499 # XXX this loop body doesn't seem to be executed?
1500 no1 = n1.notations.item(i)
1501 no2 = n1.notations.item(i)
1502 self.assertEqual(no1.name, no2.name)
1503 self.assertEqual(no1.publicId, no2.publicId)
1504 self.assertEqual(no1.systemId, no2.systemId)
1505 stack.append((no1, no2))
1506 for i in range(len(n1.entities)):
1507 e1 = n1.entities.item(i)
1508 e2 = n2.entities.item(i)
1509 self.assertEqual(e1.notationName, e2.notationName)
1510 self.assertEqual(e1.publicId, e2.publicId)
1511 self.assertEqual(e1.systemId, e2.systemId)
1512 stack.append((e1, e2))
1513 if n1.nodeType != Node.DOCUMENT_NODE:
1514 self.assertTrue(n1.ownerDocument.isSameNode(doc))
1515 self.assertTrue(n2.ownerDocument.isSameNode(doc2))
1516 for i in range(len(n1.childNodes)):
1517 stack.append((n1.childNodes[i], n2.childNodes[i]))
1518
Guido van Rossumd8faa362007-04-27 19:54:29 +00001519 def testPickledDocument(self):
Serhiy Storchakac4722462015-11-26 23:49:42 +02001520 doc = parseString(sample)
Serhiy Storchakabad12572014-12-15 14:03:42 +02001521 for proto in range(2, pickle.HIGHEST_PROTOCOL + 1):
1522 s = pickle.dumps(doc, proto)
1523 doc2 = pickle.loads(s)
Serhiy Storchakac4722462015-11-26 23:49:42 +02001524 self.assert_recursive_equal(doc, doc2)
1525
1526 def testDeepcopiedDocument(self):
1527 doc = parseString(sample)
1528 doc2 = copy.deepcopy(doc)
1529 self.assert_recursive_equal(doc, doc2)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001530
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001531 def testSerializeCommentNodeWithDoubleHyphen(self):
1532 doc = create_doc_without_doctype()
1533 doc.appendChild(doc.createComment("foo--bar"))
1534 self.assertRaises(ValueError, doc.toxml)
1535
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001536
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001537 def testEmptyXMLNSValue(self):
1538 doc = parseString("<element xmlns=''>\n"
1539 "<foo/>\n</element>")
1540 doc2 = parseString(doc.toxml())
1541 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1542
R David Murray9077d242014-04-20 00:46:05 -04001543 def testExceptionOnSpacesInXMLNSValue(self):
1544 with self.assertRaisesRegex(ValueError, 'Unsupported syntax'):
1545 parseString('<element xmlns:abc="http:abc.com/de f g/hi/j k"><abc:foo /></element>')
1546
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001547 def testDocRemoveChild(self):
1548 doc = parse(tstfile)
1549 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1550 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1551 num_children_before = len(doc.childNodes)
1552 doc.removeChild(doc.childNodes[0])
1553 num_children_after = len(doc.childNodes)
1554 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001555
Raymond Hettinger92a40552014-06-15 14:48:19 -07001556 def testProcessingInstructionNameError(self):
1557 # wrong variable in .nodeValue property will
1558 # lead to "NameError: name 'data' is not defined"
1559 doc = parse(tstfile)
1560 pi = doc.createProcessingInstruction("y", "z")
1561 pi.nodeValue = "crash"
1562
Guido van Rossumd8faa362007-04-27 19:54:29 +00001563if __name__ == "__main__":
Zachary Ware38c707e2015-04-13 15:00:43 -05001564 unittest.main()