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