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