blob: 5867b2dcba23ae79a03930e1cf8e574ee51e78d6 [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
Florent Xiclunaf15351d2010-03-13 23:24:31 +00004from test.support import verbose, 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()
313 child2 = dom2.appendChild(dom.createElement("foo"))
314 self.assertRaises(xml.dom.NotFoundErr, child.removeAttributeNode,
315 node)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000316 dom.unlink()
317
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200318 def testHasAttribute(self):
319 dom = Document()
320 child = dom.appendChild(dom.createElement("foo"))
321 child.setAttribute("spam", "jam")
322 self.confirm(child.hasAttribute("spam"))
323
Guido van Rossumd8faa362007-04-27 19:54:29 +0000324 def testChangeAttr(self):
325 dom = parseString("<abc/>")
326 el = dom.documentElement
327 el.setAttribute("spam", "jam")
328 self.confirm(len(el.attributes) == 1)
329 el.setAttribute("spam", "bam")
330 # Set this attribute to be an ID and make sure that doesn't change
331 # when changing the value:
332 el.setIdAttribute("spam")
333 self.confirm(len(el.attributes) == 1
334 and el.attributes["spam"].value == "bam"
335 and el.attributes["spam"].nodeValue == "bam"
336 and el.getAttribute("spam") == "bam"
337 and el.getAttributeNode("spam").isId)
338 el.attributes["spam"] = "ham"
339 self.confirm(len(el.attributes) == 1
340 and el.attributes["spam"].value == "ham"
341 and el.attributes["spam"].nodeValue == "ham"
342 and el.getAttribute("spam") == "ham"
343 and el.attributes["spam"].isId)
344 el.setAttribute("spam2", "bam")
345 self.confirm(len(el.attributes) == 2
346 and el.attributes["spam"].value == "ham"
347 and el.attributes["spam"].nodeValue == "ham"
348 and el.getAttribute("spam") == "ham"
349 and el.attributes["spam2"].value == "bam"
350 and el.attributes["spam2"].nodeValue == "bam"
351 and el.getAttribute("spam2") == "bam")
352 el.attributes["spam2"] = "bam2"
353 self.confirm(len(el.attributes) == 2
354 and el.attributes["spam"].value == "ham"
355 and el.attributes["spam"].nodeValue == "ham"
356 and el.getAttribute("spam") == "ham"
357 and el.attributes["spam2"].value == "bam2"
358 and el.attributes["spam2"].nodeValue == "bam2"
359 and el.getAttribute("spam2") == "bam2")
360 dom.unlink()
361
362 def testGetAttrList(self):
363 pass
364
Martin v. Löwis67245a62012-03-05 07:01:49 +0100365 def testGetAttrValues(self):
366 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000367
Martin v. Löwis67245a62012-03-05 07:01:49 +0100368 def testGetAttrLength(self):
369 pass
Guido van Rossumd8faa362007-04-27 19:54:29 +0000370
Martin v. Löwis67245a62012-03-05 07:01:49 +0100371 def testGetAttribute(self):
372 dom = Document()
373 child = dom.appendChild(
374 dom.createElementNS("http://www.python.org", "python:abc"))
375 self.assertEqual(child.getAttribute('missing'), '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000376
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200377 def testGetAttributeNS(self):
378 dom = Document()
379 child = dom.appendChild(
380 dom.createElementNS("http://www.python.org", "python:abc"))
381 child.setAttributeNS("http://www.w3.org", "xmlns:python",
382 "http://www.python.org")
383 self.assertEqual(child.getAttributeNS("http://www.w3.org", "python"),
384 'http://www.python.org')
385 self.assertEqual(child.getAttributeNS("http://www.w3.org", "other"),
386 '')
Martin v. Löwis67245a62012-03-05 07:01:49 +0100387 child2 = child.appendChild(dom.createElement('abc'))
388 self.assertEqual(child2.getAttributeNS("http://www.python.org", "missing"),
389 '')
Guido van Rossumd8faa362007-04-27 19:54:29 +0000390
391 def testGetAttributeNode(self): pass
392
393 def testGetElementsByTagNameNS(self):
394 d="""<foo xmlns:minidom='http://pyxml.sf.net/minidom'>
395 <minidom:myelem/>
396 </foo>"""
397 dom = parseString(d)
398 elems = dom.getElementsByTagNameNS("http://pyxml.sf.net/minidom",
399 "myelem")
400 self.confirm(len(elems) == 1
401 and elems[0].namespaceURI == "http://pyxml.sf.net/minidom"
402 and elems[0].localName == "myelem"
403 and elems[0].prefix == "minidom"
404 and elems[0].tagName == "minidom:myelem"
405 and elems[0].nodeName == "minidom:myelem")
406 dom.unlink()
407
408 def get_empty_nodelist_from_elements_by_tagName_ns_helper(self, doc, nsuri,
409 lname):
410 nodelist = doc.getElementsByTagNameNS(nsuri, lname)
411 self.confirm(len(nodelist) == 0)
412
413 def testGetEmptyNodeListFromElementsByTagNameNS(self):
414 doc = parseString('<doc/>')
415 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
416 doc, 'http://xml.python.org/namespaces/a', 'localname')
417 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
418 doc, '*', 'splat')
419 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
420 doc, 'http://xml.python.org/namespaces/a', '*')
421
422 doc = parseString('<doc xmlns="http://xml.python.org/splat"><e/></doc>')
423 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
424 doc, "http://xml.python.org/splat", "not-there")
425 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
426 doc, "*", "not-there")
427 self.get_empty_nodelist_from_elements_by_tagName_ns_helper(
428 doc, "http://somewhere.else.net/not-there", "e")
429
430 def testElementReprAndStr(self):
431 dom = Document()
432 el = dom.appendChild(dom.createElement("abc"))
433 string1 = repr(el)
434 string2 = str(el)
435 self.confirm(string1 == string2)
436 dom.unlink()
437
438 def testElementReprAndStrUnicode(self):
439 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000440 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000441 string1 = repr(el)
442 string2 = str(el)
443 self.confirm(string1 == string2)
444 dom.unlink()
445
446 def testElementReprAndStrUnicodeNS(self):
447 dom = Document()
448 el = dom.appendChild(
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000449 dom.createElementNS("http://www.slashdot.org", "slash:abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000450 string1 = repr(el)
451 string2 = str(el)
452 self.confirm(string1 == string2)
Ezio Melotti7fb4da72010-03-18 12:29:13 +0000453 self.confirm("slash:abc" in string1)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000454 dom.unlink()
455
456 def testAttributeRepr(self):
457 dom = Document()
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000458 el = dom.appendChild(dom.createElement("abc"))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000459 node = el.setAttribute("abc", "def")
460 self.confirm(str(node) == repr(node))
461 dom.unlink()
462
463 def testTextNodeRepr(self): pass
464
465 def testWriteXML(self):
466 str = '<?xml version="1.0" ?><a b="c"/>'
467 dom = parseString(str)
468 domstr = dom.toxml()
469 dom.unlink()
470 self.confirm(str == domstr)
471
472 def testAltNewline(self):
473 str = '<?xml version="1.0" ?>\n<a b="c"/>\n'
474 dom = parseString(str)
475 domstr = dom.toprettyxml(newl="\r\n")
476 dom.unlink()
477 self.confirm(domstr == str.replace("\n", "\r\n"))
478
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200479 def test_toprettyxml_with_text_nodes(self):
480 # see issue #4147, text nodes are not indented
481 decl = '<?xml version="1.0" ?>\n'
482 self.assertEqual(parseString('<B>A</B>').toprettyxml(),
483 decl + '<B>A</B>\n')
484 self.assertEqual(parseString('<C>A<B>A</B></C>').toprettyxml(),
485 decl + '<C>\n\tA\n\t<B>A</B>\n</C>\n')
486 self.assertEqual(parseString('<C><B>A</B>A</C>').toprettyxml(),
487 decl + '<C>\n\t<B>A</B>\n\tA\n</C>\n')
488 self.assertEqual(parseString('<C><B>A</B><B>A</B></C>').toprettyxml(),
489 decl + '<C>\n\t<B>A</B>\n\t<B>A</B>\n</C>\n')
490 self.assertEqual(parseString('<C><B>A</B>A<B>A</B></C>').toprettyxml(),
491 decl + '<C>\n\t<B>A</B>\n\tA\n\t<B>A</B>\n</C>\n')
492
493 def test_toprettyxml_with_adjacent_text_nodes(self):
494 # see issue #4147, adjacent text nodes are indented normally
495 dom = Document()
496 elem = dom.createElement('elem')
497 elem.appendChild(dom.createTextNode('TEXT'))
498 elem.appendChild(dom.createTextNode('TEXT'))
499 dom.appendChild(elem)
500 decl = '<?xml version="1.0" ?>\n'
501 self.assertEqual(dom.toprettyxml(),
502 decl + '<elem>\n\tTEXT\n\tTEXT\n</elem>\n')
503
Éric Araujo63ba97b2011-10-05 01:29:22 +0200504 def test_toprettyxml_preserves_content_of_text_node(self):
Ezio Melotti8008f2a2011-11-18 17:34:26 +0200505 # see issue #4147
506 for str in ('<B>A</B>', '<A><B>C</B></A>'):
507 dom = parseString(str)
508 dom2 = parseString(dom.toprettyxml())
509 self.assertEqual(
510 dom.getElementsByTagName('B')[0].childNodes[0].toxml(),
511 dom2.getElementsByTagName('B')[0].childNodes[0].toxml())
R David Murray791744b2011-10-01 16:19:51 -0400512
Guido van Rossumd8faa362007-04-27 19:54:29 +0000513 def testProcessingInstruction(self):
514 dom = parseString('<e><?mypi \t\n data \t\n ?></e>')
515 pi = dom.documentElement.firstChild
516 self.confirm(pi.target == "mypi"
517 and pi.data == "data \t\n "
518 and pi.nodeName == "mypi"
519 and pi.nodeType == Node.PROCESSING_INSTRUCTION_NODE
520 and pi.attributes is None
521 and not pi.hasChildNodes()
522 and len(pi.childNodes) == 0
523 and pi.firstChild is None
524 and pi.lastChild is None
525 and pi.localName is None
526 and pi.namespaceURI == xml.dom.EMPTY_NAMESPACE)
527
528 def testProcessingInstructionRepr(self): pass
529
530 def testTextRepr(self): pass
531
532 def testWriteText(self): pass
533
534 def testDocumentElement(self): pass
535
536 def testTooManyDocumentElements(self):
537 doc = parseString("<doc/>")
538 elem = doc.createElement("extra")
539 # Should raise an exception when adding an extra document element.
540 self.assertRaises(xml.dom.HierarchyRequestErr, doc.appendChild, elem)
541 elem.unlink()
542 doc.unlink()
543
544 def testCreateElementNS(self): pass
545
546 def testCreateAttributeNS(self): pass
547
548 def testParse(self): pass
549
550 def testParseString(self): pass
551
552 def testComment(self): pass
553
554 def testAttrListItem(self): pass
555
556 def testAttrListItems(self): pass
557
558 def testAttrListItemNS(self): pass
559
560 def testAttrListKeys(self): pass
561
562 def testAttrListKeysNS(self): pass
563
564 def testRemoveNamedItem(self):
565 doc = parseString("<doc a=''/>")
566 e = doc.documentElement
567 attrs = e.attributes
568 a1 = e.getAttributeNode("a")
569 a2 = attrs.removeNamedItem("a")
570 self.confirm(a1.isSameNode(a2))
571 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItem, "a")
572
573 def testRemoveNamedItemNS(self):
574 doc = parseString("<doc xmlns:a='http://xml.python.org/' a:b=''/>")
575 e = doc.documentElement
576 attrs = e.attributes
577 a1 = e.getAttributeNodeNS("http://xml.python.org/", "b")
578 a2 = attrs.removeNamedItemNS("http://xml.python.org/", "b")
579 self.confirm(a1.isSameNode(a2))
580 self.assertRaises(xml.dom.NotFoundErr, attrs.removeNamedItemNS,
581 "http://xml.python.org/", "b")
582
583 def testAttrListValues(self): pass
584
585 def testAttrListLength(self): pass
586
587 def testAttrList__getitem__(self): pass
588
589 def testAttrList__setitem__(self): pass
590
591 def testSetAttrValueandNodeValue(self): pass
592
593 def testParseElement(self): pass
594
595 def testParseAttributes(self): pass
596
597 def testParseElementNamespaces(self): pass
598
599 def testParseAttributeNamespaces(self): pass
600
601 def testParseProcessingInstructions(self): pass
602
603 def testChildNodes(self): pass
604
605 def testFirstChild(self): pass
606
Raymond Hettinger06eef9c2011-06-25 15:54:52 +0200607 def testHasChildNodes(self):
608 dom = parseString("<doc><foo/></doc>")
609 doc = dom.documentElement
610 self.assertTrue(dom.hasChildNodes())
611 dom2 = parseString("<doc/>")
612 doc2 = dom2.documentElement
613 self.assertFalse(doc2.hasChildNodes())
Guido van Rossumd8faa362007-04-27 19:54:29 +0000614
615 def _testCloneElementCopiesAttributes(self, e1, e2, test):
616 attrs1 = e1.attributes
617 attrs2 = e2.attributes
618 keys1 = list(attrs1.keys())
619 keys2 = list(attrs2.keys())
620 keys1.sort()
621 keys2.sort()
622 self.confirm(keys1 == keys2, "clone of element has same attribute keys")
623 for i in range(len(keys1)):
624 a1 = attrs1.item(i)
625 a2 = attrs2.item(i)
626 self.confirm(a1 is not a2
627 and a1.value == a2.value
628 and a1.nodeValue == a2.nodeValue
629 and a1.namespaceURI == a2.namespaceURI
630 and a1.localName == a2.localName
631 , "clone of attribute node has proper attribute values")
632 self.confirm(a2.ownerElement is e2,
633 "clone of attribute node correctly owned")
634
635 def _setupCloneElement(self, deep):
636 dom = parseString("<doc attr='value'><foo/></doc>")
637 root = dom.documentElement
638 clone = root.cloneNode(deep)
639 self._testCloneElementCopiesAttributes(
640 root, clone, "testCloneElement" + (deep and "Deep" or "Shallow"))
641 # mutilate the original so shared data is detected
642 root.tagName = root.nodeName = "MODIFIED"
643 root.setAttribute("attr", "NEW VALUE")
644 root.setAttribute("added", "VALUE")
645 return dom, clone
646
647 def testCloneElementShallow(self):
648 dom, clone = self._setupCloneElement(0)
649 self.confirm(len(clone.childNodes) == 0
650 and clone.childNodes.length == 0
651 and clone.parentNode is None
652 and clone.toxml() == '<doc attr="value"/>'
653 , "testCloneElementShallow")
654 dom.unlink()
655
656 def testCloneElementDeep(self):
657 dom, clone = self._setupCloneElement(1)
658 self.confirm(len(clone.childNodes) == 1
659 and clone.childNodes.length == 1
660 and clone.parentNode is None
661 and clone.toxml() == '<doc attr="value"><foo/></doc>'
662 , "testCloneElementDeep")
663 dom.unlink()
664
665 def testCloneDocumentShallow(self):
666 doc = parseString("<?xml version='1.0'?>\n"
667 "<!-- comment -->"
668 "<!DOCTYPE doc [\n"
669 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
670 "]>\n"
671 "<doc attr='value'/>")
672 doc2 = doc.cloneNode(0)
673 self.confirm(doc2 is None,
674 "testCloneDocumentShallow:"
675 " shallow cloning of documents makes no sense!")
676
677 def testCloneDocumentDeep(self):
678 doc = parseString("<?xml version='1.0'?>\n"
679 "<!-- comment -->"
680 "<!DOCTYPE doc [\n"
681 "<!NOTATION notation SYSTEM 'http://xml.python.org/'>\n"
682 "]>\n"
683 "<doc attr='value'/>")
684 doc2 = doc.cloneNode(1)
685 self.confirm(not (doc.isSameNode(doc2) or doc2.isSameNode(doc)),
686 "testCloneDocumentDeep: document objects not distinct")
687 self.confirm(len(doc.childNodes) == len(doc2.childNodes),
688 "testCloneDocumentDeep: wrong number of Document children")
689 self.confirm(doc2.documentElement.nodeType == Node.ELEMENT_NODE,
690 "testCloneDocumentDeep: documentElement not an ELEMENT_NODE")
691 self.confirm(doc2.documentElement.ownerDocument.isSameNode(doc2),
692 "testCloneDocumentDeep: documentElement owner is not new document")
693 self.confirm(not doc.documentElement.isSameNode(doc2.documentElement),
694 "testCloneDocumentDeep: documentElement should not be shared")
695 if doc.doctype is not None:
696 # check the doctype iff the original DOM maintained it
697 self.confirm(doc2.doctype.nodeType == Node.DOCUMENT_TYPE_NODE,
698 "testCloneDocumentDeep: doctype not a DOCUMENT_TYPE_NODE")
699 self.confirm(doc2.doctype.ownerDocument.isSameNode(doc2))
700 self.confirm(not doc.doctype.isSameNode(doc2.doctype))
701
702 def testCloneDocumentTypeDeepOk(self):
703 doctype = create_nonempty_doctype()
704 clone = doctype.cloneNode(1)
705 self.confirm(clone is not None
706 and clone.nodeName == doctype.nodeName
707 and clone.name == doctype.name
708 and clone.publicId == doctype.publicId
709 and clone.systemId == doctype.systemId
710 and len(clone.entities) == len(doctype.entities)
711 and clone.entities.item(len(clone.entities)) is None
712 and len(clone.notations) == len(doctype.notations)
713 and clone.notations.item(len(clone.notations)) is None
714 and len(clone.childNodes) == 0)
715 for i in range(len(doctype.entities)):
716 se = doctype.entities.item(i)
717 ce = clone.entities.item(i)
718 self.confirm((not se.isSameNode(ce))
719 and (not ce.isSameNode(se))
720 and ce.nodeName == se.nodeName
721 and ce.notationName == se.notationName
722 and ce.publicId == se.publicId
723 and ce.systemId == se.systemId
724 and ce.encoding == se.encoding
725 and ce.actualEncoding == se.actualEncoding
726 and ce.version == se.version)
727 for i in range(len(doctype.notations)):
728 sn = doctype.notations.item(i)
729 cn = clone.notations.item(i)
730 self.confirm((not sn.isSameNode(cn))
731 and (not cn.isSameNode(sn))
732 and cn.nodeName == sn.nodeName
733 and cn.publicId == sn.publicId
734 and cn.systemId == sn.systemId)
735
736 def testCloneDocumentTypeDeepNotOk(self):
737 doc = create_doc_with_doctype()
738 clone = doc.doctype.cloneNode(1)
739 self.confirm(clone is None, "testCloneDocumentTypeDeepNotOk")
740
741 def testCloneDocumentTypeShallowOk(self):
742 doctype = create_nonempty_doctype()
743 clone = doctype.cloneNode(0)
744 self.confirm(clone is not None
745 and clone.nodeName == doctype.nodeName
746 and clone.name == doctype.name
747 and clone.publicId == doctype.publicId
748 and clone.systemId == doctype.systemId
749 and len(clone.entities) == 0
750 and clone.entities.item(0) is None
751 and len(clone.notations) == 0
752 and clone.notations.item(0) is None
753 and len(clone.childNodes) == 0)
754
755 def testCloneDocumentTypeShallowNotOk(self):
756 doc = create_doc_with_doctype()
757 clone = doc.doctype.cloneNode(0)
758 self.confirm(clone is None, "testCloneDocumentTypeShallowNotOk")
759
760 def check_import_document(self, deep, testName):
761 doc1 = parseString("<doc/>")
762 doc2 = parseString("<doc/>")
763 self.assertRaises(xml.dom.NotSupportedErr, doc1.importNode, doc2, deep)
764
765 def testImportDocumentShallow(self):
766 self.check_import_document(0, "testImportDocumentShallow")
767
768 def testImportDocumentDeep(self):
769 self.check_import_document(1, "testImportDocumentDeep")
770
771 def testImportDocumentTypeShallow(self):
772 src = create_doc_with_doctype()
773 target = create_doc_without_doctype()
774 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
775 src.doctype, 0)
776
777 def testImportDocumentTypeDeep(self):
778 src = create_doc_with_doctype()
779 target = create_doc_without_doctype()
780 self.assertRaises(xml.dom.NotSupportedErr, target.importNode,
781 src.doctype, 1)
782
783 # Testing attribute clones uses a helper, and should always be deep,
784 # even if the argument to cloneNode is false.
785 def check_clone_attribute(self, deep, testName):
786 doc = parseString("<doc attr='value'/>")
787 attr = doc.documentElement.getAttributeNode("attr")
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000788 self.assertNotEqual(attr, None)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000789 clone = attr.cloneNode(deep)
790 self.confirm(not clone.isSameNode(attr))
791 self.confirm(not attr.isSameNode(clone))
792 self.confirm(clone.ownerElement is None,
793 testName + ": ownerElement should be None")
794 self.confirm(clone.ownerDocument.isSameNode(attr.ownerDocument),
795 testName + ": ownerDocument does not match")
796 self.confirm(clone.specified,
797 testName + ": cloned attribute must have specified == True")
798
799 def testCloneAttributeShallow(self):
800 self.check_clone_attribute(0, "testCloneAttributeShallow")
801
802 def testCloneAttributeDeep(self):
803 self.check_clone_attribute(1, "testCloneAttributeDeep")
804
805 def check_clone_pi(self, deep, testName):
806 doc = parseString("<?target data?><doc/>")
807 pi = doc.firstChild
Ezio Melottib3aedd42010-11-20 19:04:17 +0000808 self.assertEqual(pi.nodeType, Node.PROCESSING_INSTRUCTION_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000809 clone = pi.cloneNode(deep)
810 self.confirm(clone.target == pi.target
811 and clone.data == pi.data)
812
813 def testClonePIShallow(self):
814 self.check_clone_pi(0, "testClonePIShallow")
815
816 def testClonePIDeep(self):
817 self.check_clone_pi(1, "testClonePIDeep")
818
819 def testNormalize(self):
820 doc = parseString("<doc/>")
821 root = doc.documentElement
822 root.appendChild(doc.createTextNode("first"))
823 root.appendChild(doc.createTextNode("second"))
824 self.confirm(len(root.childNodes) == 2
825 and root.childNodes.length == 2,
826 "testNormalize -- preparation")
827 doc.normalize()
828 self.confirm(len(root.childNodes) == 1
829 and root.childNodes.length == 1
830 and root.firstChild is root.lastChild
831 and root.firstChild.data == "firstsecond"
832 , "testNormalize -- result")
833 doc.unlink()
834
835 doc = parseString("<doc/>")
836 root = doc.documentElement
837 root.appendChild(doc.createTextNode(""))
838 doc.normalize()
839 self.confirm(len(root.childNodes) == 0
840 and root.childNodes.length == 0,
841 "testNormalize -- single empty node removed")
842 doc.unlink()
843
R. David Murraydc6da8a2009-04-09 22:16:43 +0000844 def testNormalizeCombineAndNextSibling(self):
845 doc = parseString("<doc/>")
846 root = doc.documentElement
847 root.appendChild(doc.createTextNode("first"))
848 root.appendChild(doc.createTextNode("second"))
849 root.appendChild(doc.createElement("i"))
850 self.confirm(len(root.childNodes) == 3
851 and root.childNodes.length == 3,
852 "testNormalizeCombineAndNextSibling -- preparation")
853 doc.normalize()
854 self.confirm(len(root.childNodes) == 2
855 and root.childNodes.length == 2
856 and root.firstChild.data == "firstsecond"
857 and root.firstChild is not root.lastChild
858 and root.firstChild.nextSibling is root.lastChild
859 and root.firstChild.previousSibling is None
860 and root.lastChild.previousSibling is root.firstChild
861 and root.lastChild.nextSibling is None
862 , "testNormalizeCombinedAndNextSibling -- result")
863 doc.unlink()
864
865 def testNormalizeDeleteWithPrevSibling(self):
866 doc = parseString("<doc/>")
867 root = doc.documentElement
868 root.appendChild(doc.createTextNode("first"))
869 root.appendChild(doc.createTextNode(""))
870 self.confirm(len(root.childNodes) == 2
871 and root.childNodes.length == 2,
872 "testNormalizeDeleteWithPrevSibling -- preparation")
873 doc.normalize()
874 self.confirm(len(root.childNodes) == 1
875 and root.childNodes.length == 1
876 and root.firstChild.data == "first"
877 and root.firstChild is root.lastChild
878 and root.firstChild.nextSibling is None
879 and root.firstChild.previousSibling is None
880 , "testNormalizeDeleteWithPrevSibling -- result")
881 doc.unlink()
882
883 def testNormalizeDeleteWithNextSibling(self):
884 doc = parseString("<doc/>")
885 root = doc.documentElement
886 root.appendChild(doc.createTextNode(""))
887 root.appendChild(doc.createTextNode("second"))
888 self.confirm(len(root.childNodes) == 2
889 and root.childNodes.length == 2,
890 "testNormalizeDeleteWithNextSibling -- preparation")
891 doc.normalize()
892 self.confirm(len(root.childNodes) == 1
893 and root.childNodes.length == 1
894 and root.firstChild.data == "second"
895 and root.firstChild is root.lastChild
896 and root.firstChild.nextSibling is None
897 and root.firstChild.previousSibling is None
898 , "testNormalizeDeleteWithNextSibling -- result")
899 doc.unlink()
900
901 def testNormalizeDeleteWithTwoNonTextSiblings(self):
902 doc = parseString("<doc/>")
903 root = doc.documentElement
904 root.appendChild(doc.createElement("i"))
905 root.appendChild(doc.createTextNode(""))
906 root.appendChild(doc.createElement("i"))
907 self.confirm(len(root.childNodes) == 3
908 and root.childNodes.length == 3,
909 "testNormalizeDeleteWithTwoSiblings -- preparation")
910 doc.normalize()
911 self.confirm(len(root.childNodes) == 2
912 and root.childNodes.length == 2
913 and root.firstChild is not root.lastChild
914 and root.firstChild.nextSibling is root.lastChild
915 and root.firstChild.previousSibling is None
916 and root.lastChild.previousSibling is root.firstChild
917 and root.lastChild.nextSibling is None
918 , "testNormalizeDeleteWithTwoSiblings -- result")
919 doc.unlink()
920
921 def testNormalizeDeleteAndCombine(self):
922 doc = parseString("<doc/>")
923 root = doc.documentElement
924 root.appendChild(doc.createTextNode(""))
925 root.appendChild(doc.createTextNode("second"))
926 root.appendChild(doc.createTextNode(""))
927 root.appendChild(doc.createTextNode("fourth"))
928 root.appendChild(doc.createTextNode(""))
929 self.confirm(len(root.childNodes) == 5
930 and root.childNodes.length == 5,
931 "testNormalizeDeleteAndCombine -- preparation")
932 doc.normalize()
933 self.confirm(len(root.childNodes) == 1
934 and root.childNodes.length == 1
935 and root.firstChild is root.lastChild
936 and root.firstChild.data == "secondfourth"
937 and root.firstChild.previousSibling is None
938 and root.firstChild.nextSibling is None
939 , "testNormalizeDeleteAndCombine -- result")
940 doc.unlink()
941
942 def testNormalizeRecursion(self):
943 doc = parseString("<doc>"
944 "<o>"
945 "<i/>"
946 "t"
947 #
948 #x
949 "</o>"
950 "<o>"
951 "<o>"
952 "t2"
953 #x2
954 "</o>"
955 "t3"
956 #x3
957 "</o>"
958 #
959 "</doc>")
960 root = doc.documentElement
961 root.childNodes[0].appendChild(doc.createTextNode(""))
962 root.childNodes[0].appendChild(doc.createTextNode("x"))
963 root.childNodes[1].childNodes[0].appendChild(doc.createTextNode("x2"))
964 root.childNodes[1].appendChild(doc.createTextNode("x3"))
965 root.appendChild(doc.createTextNode(""))
966 self.confirm(len(root.childNodes) == 3
967 and root.childNodes.length == 3
968 and len(root.childNodes[0].childNodes) == 4
969 and root.childNodes[0].childNodes.length == 4
970 and len(root.childNodes[1].childNodes) == 3
971 and root.childNodes[1].childNodes.length == 3
972 and len(root.childNodes[1].childNodes[0].childNodes) == 2
973 and root.childNodes[1].childNodes[0].childNodes.length == 2
974 , "testNormalize2 -- preparation")
975 doc.normalize()
976 self.confirm(len(root.childNodes) == 2
977 and root.childNodes.length == 2
978 and len(root.childNodes[0].childNodes) == 2
979 and root.childNodes[0].childNodes.length == 2
980 and len(root.childNodes[1].childNodes) == 2
981 and root.childNodes[1].childNodes.length == 2
982 and len(root.childNodes[1].childNodes[0].childNodes) == 1
983 and root.childNodes[1].childNodes[0].childNodes.length == 1
984 , "testNormalize2 -- childNodes lengths")
985 self.confirm(root.childNodes[0].childNodes[1].data == "tx"
986 and root.childNodes[1].childNodes[0].childNodes[0].data == "t2x2"
987 and root.childNodes[1].childNodes[1].data == "t3x3"
988 , "testNormalize2 -- joined text fields")
989 self.confirm(root.childNodes[0].childNodes[1].nextSibling is None
990 and root.childNodes[0].childNodes[1].previousSibling
991 is root.childNodes[0].childNodes[0]
992 and root.childNodes[0].childNodes[0].previousSibling is None
993 and root.childNodes[0].childNodes[0].nextSibling
994 is root.childNodes[0].childNodes[1]
995 and root.childNodes[1].childNodes[1].nextSibling is None
996 and root.childNodes[1].childNodes[1].previousSibling
997 is root.childNodes[1].childNodes[0]
998 and root.childNodes[1].childNodes[0].previousSibling is None
999 and root.childNodes[1].childNodes[0].nextSibling
1000 is root.childNodes[1].childNodes[1]
1001 , "testNormalize2 -- sibling pointers")
1002 doc.unlink()
1003
1004
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001005 def testBug0777884(self):
1006 doc = parseString("<o>text</o>")
1007 text = doc.documentElement.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001008 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Andrew M. Kuchling688b9e32010-07-25 23:38:47 +00001009 # Should run quietly, doing nothing.
1010 text.normalize()
1011 doc.unlink()
1012
Christian Heimes05e8be12008-02-23 18:30:17 +00001013 def testBug1433694(self):
1014 doc = parseString("<o><i/>t</o>")
1015 node = doc.documentElement
1016 node.childNodes[1].nodeValue = ""
1017 node.normalize()
Florent Xicluna9b86b9a2010-03-19 19:00:44 +00001018 self.confirm(node.childNodes[-1].nextSibling is None,
Christian Heimes05e8be12008-02-23 18:30:17 +00001019 "Final child's .nextSibling should be None")
1020
Guido van Rossumd8faa362007-04-27 19:54:29 +00001021 def testSiblings(self):
1022 doc = parseString("<doc><?pi?>text?<elm/></doc>")
1023 root = doc.documentElement
1024 (pi, text, elm) = root.childNodes
1025
1026 self.confirm(pi.nextSibling is text and
1027 pi.previousSibling is None and
1028 text.nextSibling is elm and
1029 text.previousSibling is pi and
1030 elm.nextSibling is None and
1031 elm.previousSibling is text, "testSiblings")
1032
1033 doc.unlink()
1034
1035 def testParents(self):
1036 doc = parseString(
1037 "<doc><elm1><elm2/><elm2><elm3/></elm2></elm1></doc>")
1038 root = doc.documentElement
1039 elm1 = root.childNodes[0]
1040 (elm2a, elm2b) = elm1.childNodes
1041 elm3 = elm2b.childNodes[0]
1042
1043 self.confirm(root.parentNode is doc and
1044 elm1.parentNode is root and
1045 elm2a.parentNode is elm1 and
1046 elm2b.parentNode is elm1 and
1047 elm3.parentNode is elm2b, "testParents")
1048 doc.unlink()
1049
1050 def testNodeListItem(self):
1051 doc = parseString("<doc><e/><e/></doc>")
1052 children = doc.childNodes
1053 docelem = children[0]
1054 self.confirm(children[0] is children.item(0)
1055 and children.item(1) is None
1056 and docelem.childNodes.item(0) is docelem.childNodes[0]
1057 and docelem.childNodes.item(1) is docelem.childNodes[1]
1058 and docelem.childNodes.item(0).childNodes.item(0) is None,
1059 "test NodeList.item()")
1060 doc.unlink()
1061
Guido van Rossumd8faa362007-04-27 19:54:29 +00001062 def testEncodings(self):
1063 doc = parseString('<foo>&#x20ac;</foo>')
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001064 self.assertEqual(doc.toxml(),
1065 '<?xml version="1.0" ?><foo>\u20ac</foo>')
1066 self.assertEqual(doc.toxml('utf-8'),
1067 b'<?xml version="1.0" encoding="utf-8"?><foo>\xe2\x82\xac</foo>')
1068 self.assertEqual(doc.toxml('iso-8859-15'),
1069 b'<?xml version="1.0" encoding="iso-8859-15"?><foo>\xa4</foo>')
Eli Bendersky8a805022012-07-13 09:52:39 +03001070 self.assertEqual(doc.toxml('us-ascii'),
1071 b'<?xml version="1.0" encoding="us-ascii"?><foo>&#8364;</foo>')
1072 self.assertEqual(doc.toxml('utf-16'),
1073 '<?xml version="1.0" encoding="utf-16"?>'
1074 '<foo>\u20ac</foo>'.encode('utf-16'))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001075
Andrew Svetlov737fb892012-12-18 21:14:22 +02001076 # Verify that character decoding errors raise exceptions instead
Guido van Rossumd8faa362007-04-27 19:54:29 +00001077 # of crashing
1078 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001079 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001080
1081 doc.unlink()
1082
1083 class UserDataHandler:
1084 called = 0
1085 def handle(self, operation, key, data, src, dst):
1086 dst.setUserData(key, data + 1, self)
1087 src.setUserData(key, None, None)
1088 self.called = 1
1089
1090 def testUserData(self):
1091 dom = Document()
1092 n = dom.createElement('e')
1093 self.confirm(n.getUserData("foo") is None)
1094 n.setUserData("foo", None, None)
1095 self.confirm(n.getUserData("foo") is None)
1096 n.setUserData("foo", 12, 12)
1097 n.setUserData("bar", 13, 13)
1098 self.confirm(n.getUserData("foo") == 12)
1099 self.confirm(n.getUserData("bar") == 13)
1100 n.setUserData("foo", None, None)
1101 self.confirm(n.getUserData("foo") is None)
1102 self.confirm(n.getUserData("bar") == 13)
1103
1104 handler = self.UserDataHandler()
1105 n.setUserData("bar", 12, handler)
1106 c = n.cloneNode(1)
1107 self.confirm(handler.called
1108 and n.getUserData("bar") is None
1109 and c.getUserData("bar") == 13)
1110 n.unlink()
1111 c.unlink()
1112 dom.unlink()
1113
1114 def checkRenameNodeSharedConstraints(self, doc, node):
1115 # Make sure illegal NS usage is detected:
1116 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1117 "http://xml.python.org/ns", "xmlns:foo")
1118 doc2 = parseString("<doc/>")
1119 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1120 xml.dom.EMPTY_NAMESPACE, "foo")
1121
1122 def testRenameAttribute(self):
1123 doc = parseString("<doc a='v'/>")
1124 elem = doc.documentElement
1125 attrmap = elem.attributes
1126 attr = elem.attributes['a']
1127
1128 # Simple renaming
1129 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1130 self.confirm(attr.name == "b"
1131 and attr.nodeName == "b"
1132 and attr.localName is None
1133 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1134 and attr.prefix is None
1135 and attr.value == "v"
1136 and elem.getAttributeNode("a") is None
1137 and elem.getAttributeNode("b").isSameNode(attr)
1138 and attrmap["b"].isSameNode(attr)
1139 and attr.ownerDocument.isSameNode(doc)
1140 and attr.ownerElement.isSameNode(elem))
1141
1142 # Rename to have a namespace, no prefix
1143 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1144 self.confirm(attr.name == "c"
1145 and attr.nodeName == "c"
1146 and attr.localName == "c"
1147 and attr.namespaceURI == "http://xml.python.org/ns"
1148 and attr.prefix is None
1149 and attr.value == "v"
1150 and elem.getAttributeNode("a") is None
1151 and elem.getAttributeNode("b") is None
1152 and elem.getAttributeNode("c").isSameNode(attr)
1153 and elem.getAttributeNodeNS(
1154 "http://xml.python.org/ns", "c").isSameNode(attr)
1155 and attrmap["c"].isSameNode(attr)
1156 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1157
1158 # Rename to have a namespace, with prefix
1159 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1160 self.confirm(attr.name == "p:d"
1161 and attr.nodeName == "p:d"
1162 and attr.localName == "d"
1163 and attr.namespaceURI == "http://xml.python.org/ns2"
1164 and attr.prefix == "p"
1165 and attr.value == "v"
1166 and elem.getAttributeNode("a") is None
1167 and elem.getAttributeNode("b") is None
1168 and elem.getAttributeNode("c") is None
1169 and elem.getAttributeNodeNS(
1170 "http://xml.python.org/ns", "c") is None
1171 and elem.getAttributeNode("p:d").isSameNode(attr)
1172 and elem.getAttributeNodeNS(
1173 "http://xml.python.org/ns2", "d").isSameNode(attr)
1174 and attrmap["p:d"].isSameNode(attr)
1175 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1176
1177 # Rename back to a simple non-NS node
1178 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1179 self.confirm(attr.name == "e"
1180 and attr.nodeName == "e"
1181 and attr.localName is None
1182 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1183 and attr.prefix is None
1184 and attr.value == "v"
1185 and elem.getAttributeNode("a") is None
1186 and elem.getAttributeNode("b") is None
1187 and elem.getAttributeNode("c") is None
1188 and elem.getAttributeNode("p:d") is None
1189 and elem.getAttributeNodeNS(
1190 "http://xml.python.org/ns", "c") is None
1191 and elem.getAttributeNode("e").isSameNode(attr)
1192 and attrmap["e"].isSameNode(attr))
1193
1194 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1195 "http://xml.python.org/ns", "xmlns")
1196 self.checkRenameNodeSharedConstraints(doc, attr)
1197 doc.unlink()
1198
1199 def testRenameElement(self):
1200 doc = parseString("<doc/>")
1201 elem = doc.documentElement
1202
1203 # Simple renaming
1204 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1205 self.confirm(elem.tagName == "a"
1206 and elem.nodeName == "a"
1207 and elem.localName is None
1208 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1209 and elem.prefix is None
1210 and elem.ownerDocument.isSameNode(doc))
1211
1212 # Rename to have a namespace, no prefix
1213 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1214 self.confirm(elem.tagName == "b"
1215 and elem.nodeName == "b"
1216 and elem.localName == "b"
1217 and elem.namespaceURI == "http://xml.python.org/ns"
1218 and elem.prefix is None
1219 and elem.ownerDocument.isSameNode(doc))
1220
1221 # Rename to have a namespace, with prefix
1222 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1223 self.confirm(elem.tagName == "p:c"
1224 and elem.nodeName == "p:c"
1225 and elem.localName == "c"
1226 and elem.namespaceURI == "http://xml.python.org/ns2"
1227 and elem.prefix == "p"
1228 and elem.ownerDocument.isSameNode(doc))
1229
1230 # Rename back to a simple non-NS node
1231 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1232 self.confirm(elem.tagName == "d"
1233 and elem.nodeName == "d"
1234 and elem.localName is None
1235 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1236 and elem.prefix is None
1237 and elem.ownerDocument.isSameNode(doc))
1238
1239 self.checkRenameNodeSharedConstraints(doc, elem)
1240 doc.unlink()
1241
1242 def testRenameOther(self):
1243 # We have to create a comment node explicitly since not all DOM
1244 # builders used with minidom add comments to the DOM.
1245 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1246 xml.dom.EMPTY_NAMESPACE, "e", None)
1247 node = doc.createComment("comment")
1248 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1249 xml.dom.EMPTY_NAMESPACE, "foo")
1250 doc.unlink()
1251
1252 def testWholeText(self):
1253 doc = parseString("<doc>a</doc>")
1254 elem = doc.documentElement
1255 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001256 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001257
1258 self.checkWholeText(text, "a")
1259 elem.appendChild(doc.createTextNode("b"))
1260 self.checkWholeText(text, "ab")
1261 elem.insertBefore(doc.createCDATASection("c"), text)
1262 self.checkWholeText(text, "cab")
1263
1264 # make sure we don't cross other nodes
1265 splitter = doc.createComment("comment")
1266 elem.appendChild(splitter)
1267 text2 = doc.createTextNode("d")
1268 elem.appendChild(text2)
1269 self.checkWholeText(text, "cab")
1270 self.checkWholeText(text2, "d")
1271
1272 x = doc.createElement("x")
1273 elem.replaceChild(x, splitter)
1274 splitter = x
1275 self.checkWholeText(text, "cab")
1276 self.checkWholeText(text2, "d")
1277
1278 x = doc.createProcessingInstruction("y", "z")
1279 elem.replaceChild(x, splitter)
1280 splitter = x
1281 self.checkWholeText(text, "cab")
1282 self.checkWholeText(text2, "d")
1283
1284 elem.removeChild(splitter)
1285 self.checkWholeText(text, "cabd")
1286 self.checkWholeText(text2, "cabd")
1287
1288 def testPatch1094164(self):
1289 doc = parseString("<doc><e/></doc>")
1290 elem = doc.documentElement
1291 e = elem.firstChild
1292 self.confirm(e.parentNode is elem, "Before replaceChild()")
1293 # Check that replacing a child with itself leaves the tree unchanged
1294 elem.replaceChild(e, e)
1295 self.confirm(e.parentNode is elem, "After replaceChild()")
1296
1297 def testReplaceWholeText(self):
1298 def setup():
1299 doc = parseString("<doc>a<e/>d</doc>")
1300 elem = doc.documentElement
1301 text1 = elem.firstChild
1302 text2 = elem.lastChild
1303 splitter = text1.nextSibling
1304 elem.insertBefore(doc.createTextNode("b"), splitter)
1305 elem.insertBefore(doc.createCDATASection("c"), text1)
1306 return doc, elem, text1, splitter, text2
1307
1308 doc, elem, text1, splitter, text2 = setup()
1309 text = text1.replaceWholeText("new content")
1310 self.checkWholeText(text, "new content")
1311 self.checkWholeText(text2, "d")
1312 self.confirm(len(elem.childNodes) == 3)
1313
1314 doc, elem, text1, splitter, text2 = setup()
1315 text = text2.replaceWholeText("new content")
1316 self.checkWholeText(text, "new content")
1317 self.checkWholeText(text1, "cab")
1318 self.confirm(len(elem.childNodes) == 5)
1319
1320 doc, elem, text1, splitter, text2 = setup()
1321 text = text1.replaceWholeText("")
1322 self.checkWholeText(text2, "d")
1323 self.confirm(text is None
1324 and len(elem.childNodes) == 2)
1325
1326 def testSchemaType(self):
1327 doc = parseString(
1328 "<!DOCTYPE doc [\n"
1329 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1330 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1331 " <!ATTLIST doc id ID #IMPLIED \n"
1332 " ref IDREF #IMPLIED \n"
1333 " refs IDREFS #IMPLIED \n"
1334 " enum (a|b) #IMPLIED \n"
1335 " ent ENTITY #IMPLIED \n"
1336 " ents ENTITIES #IMPLIED \n"
1337 " nm NMTOKEN #IMPLIED \n"
1338 " nms NMTOKENS #IMPLIED \n"
1339 " text CDATA #IMPLIED \n"
1340 " >\n"
1341 "]><doc id='name' notid='name' text='splat!' enum='b'"
1342 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1343 " nm='123' nms='123 abc' />")
1344 elem = doc.documentElement
1345 # We don't want to rely on any specific loader at this point, so
1346 # just make sure we can get to all the names, and that the
1347 # DTD-based namespace is right. The names can vary by loader
1348 # since each supports a different level of DTD information.
1349 t = elem.schemaType
1350 self.confirm(t.name is None
1351 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1352 names = "id notid text enum ref refs ent ents nm nms".split()
1353 for name in names:
1354 a = elem.getAttributeNode(name)
1355 t = a.schemaType
1356 self.confirm(hasattr(t, "name")
1357 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1358
1359 def testSetIdAttribute(self):
1360 doc = parseString("<doc a1='v' a2='w'/>")
1361 e = doc.documentElement
1362 a1 = e.getAttributeNode("a1")
1363 a2 = e.getAttributeNode("a2")
1364 self.confirm(doc.getElementById("v") is None
1365 and not a1.isId
1366 and not a2.isId)
1367 e.setIdAttribute("a1")
1368 self.confirm(e.isSameNode(doc.getElementById("v"))
1369 and a1.isId
1370 and not a2.isId)
1371 e.setIdAttribute("a2")
1372 self.confirm(e.isSameNode(doc.getElementById("v"))
1373 and e.isSameNode(doc.getElementById("w"))
1374 and a1.isId
1375 and a2.isId)
1376 # replace the a1 node; the new node should *not* be an ID
1377 a3 = doc.createAttribute("a1")
1378 a3.value = "v"
1379 e.setAttributeNode(a3)
1380 self.confirm(doc.getElementById("v") is None
1381 and e.isSameNode(doc.getElementById("w"))
1382 and not a1.isId
1383 and a2.isId
1384 and not a3.isId)
1385 # renaming an attribute should not affect its ID-ness:
1386 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1387 self.confirm(e.isSameNode(doc.getElementById("w"))
1388 and a2.isId)
1389
1390 def testSetIdAttributeNS(self):
1391 NS1 = "http://xml.python.org/ns1"
1392 NS2 = "http://xml.python.org/ns2"
1393 doc = parseString("<doc"
1394 " xmlns:ns1='" + NS1 + "'"
1395 " xmlns:ns2='" + NS2 + "'"
1396 " ns1:a1='v' ns2:a2='w'/>")
1397 e = doc.documentElement
1398 a1 = e.getAttributeNodeNS(NS1, "a1")
1399 a2 = e.getAttributeNodeNS(NS2, "a2")
1400 self.confirm(doc.getElementById("v") is None
1401 and not a1.isId
1402 and not a2.isId)
1403 e.setIdAttributeNS(NS1, "a1")
1404 self.confirm(e.isSameNode(doc.getElementById("v"))
1405 and a1.isId
1406 and not a2.isId)
1407 e.setIdAttributeNS(NS2, "a2")
1408 self.confirm(e.isSameNode(doc.getElementById("v"))
1409 and e.isSameNode(doc.getElementById("w"))
1410 and a1.isId
1411 and a2.isId)
1412 # replace the a1 node; the new node should *not* be an ID
1413 a3 = doc.createAttributeNS(NS1, "a1")
1414 a3.value = "v"
1415 e.setAttributeNode(a3)
1416 self.confirm(e.isSameNode(doc.getElementById("w")))
1417 self.confirm(not a1.isId)
1418 self.confirm(a2.isId)
1419 self.confirm(not a3.isId)
1420 self.confirm(doc.getElementById("v") is None)
1421 # renaming an attribute should not affect its ID-ness:
1422 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1423 self.confirm(e.isSameNode(doc.getElementById("w"))
1424 and a2.isId)
1425
1426 def testSetIdAttributeNode(self):
1427 NS1 = "http://xml.python.org/ns1"
1428 NS2 = "http://xml.python.org/ns2"
1429 doc = parseString("<doc"
1430 " xmlns:ns1='" + NS1 + "'"
1431 " xmlns:ns2='" + NS2 + "'"
1432 " ns1:a1='v' ns2:a2='w'/>")
1433 e = doc.documentElement
1434 a1 = e.getAttributeNodeNS(NS1, "a1")
1435 a2 = e.getAttributeNodeNS(NS2, "a2")
1436 self.confirm(doc.getElementById("v") is None
1437 and not a1.isId
1438 and not a2.isId)
1439 e.setIdAttributeNode(a1)
1440 self.confirm(e.isSameNode(doc.getElementById("v"))
1441 and a1.isId
1442 and not a2.isId)
1443 e.setIdAttributeNode(a2)
1444 self.confirm(e.isSameNode(doc.getElementById("v"))
1445 and e.isSameNode(doc.getElementById("w"))
1446 and a1.isId
1447 and a2.isId)
1448 # replace the a1 node; the new node should *not* be an ID
1449 a3 = doc.createAttributeNS(NS1, "a1")
1450 a3.value = "v"
1451 e.setAttributeNode(a3)
1452 self.confirm(e.isSameNode(doc.getElementById("w")))
1453 self.confirm(not a1.isId)
1454 self.confirm(a2.isId)
1455 self.confirm(not a3.isId)
1456 self.confirm(doc.getElementById("v") is None)
1457 # renaming an attribute should not affect its ID-ness:
1458 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1459 self.confirm(e.isSameNode(doc.getElementById("w"))
1460 and a2.isId)
1461
1462 def testPickledDocument(self):
1463 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1464 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1465 " 'http://xml.python.org/system' [\n"
1466 " <!ELEMENT e EMPTY>\n"
1467 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1468 "]><doc attr='value'> text\n"
1469 "<?pi sample?> <!-- comment --> <e/> </doc>")
1470 s = pickle.dumps(doc)
1471 doc2 = pickle.loads(s)
1472 stack = [(doc, doc2)]
1473 while stack:
1474 n1, n2 = stack.pop()
1475 self.confirm(n1.nodeType == n2.nodeType
1476 and len(n1.childNodes) == len(n2.childNodes)
1477 and n1.nodeName == n2.nodeName
1478 and not n1.isSameNode(n2)
1479 and not n2.isSameNode(n1))
1480 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1481 len(n1.entities)
1482 len(n2.entities)
1483 len(n1.notations)
1484 len(n2.notations)
1485 self.confirm(len(n1.entities) == len(n2.entities)
1486 and len(n1.notations) == len(n2.notations))
1487 for i in range(len(n1.notations)):
Georg Brandl89fad142010-03-14 10:23:39 +00001488 # XXX this loop body doesn't seem to be executed?
Guido van Rossumd8faa362007-04-27 19:54:29 +00001489 no1 = n1.notations.item(i)
1490 no2 = n1.notations.item(i)
1491 self.confirm(no1.name == no2.name
1492 and no1.publicId == no2.publicId
1493 and no1.systemId == no2.systemId)
Georg Brandl89fad142010-03-14 10:23:39 +00001494 stack.append((no1, no2))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001495 for i in range(len(n1.entities)):
1496 e1 = n1.entities.item(i)
1497 e2 = n2.entities.item(i)
1498 self.confirm(e1.notationName == e2.notationName
1499 and e1.publicId == e2.publicId
1500 and e1.systemId == e2.systemId)
1501 stack.append((e1, e2))
1502 if n1.nodeType != Node.DOCUMENT_NODE:
1503 self.confirm(n1.ownerDocument.isSameNode(doc)
1504 and n2.ownerDocument.isSameNode(doc2))
1505 for i in range(len(n1.childNodes)):
1506 stack.append((n1.childNodes[i], n2.childNodes[i]))
1507
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001508 def testSerializeCommentNodeWithDoubleHyphen(self):
1509 doc = create_doc_without_doctype()
1510 doc.appendChild(doc.createComment("foo--bar"))
1511 self.assertRaises(ValueError, doc.toxml)
1512
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001513
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001514 def testEmptyXMLNSValue(self):
1515 doc = parseString("<element xmlns=''>\n"
1516 "<foo/>\n</element>")
1517 doc2 = parseString(doc.toxml())
1518 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1519
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001520 def testDocRemoveChild(self):
1521 doc = parse(tstfile)
1522 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1523 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1524 num_children_before = len(doc.childNodes)
1525 doc.removeChild(doc.childNodes[0])
1526 num_children_after = len(doc.childNodes)
1527 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001528
Guido van Rossumd8faa362007-04-27 19:54:29 +00001529def test_main():
1530 run_unittest(MinidomTest)
1531
1532if __name__ == "__main__":
1533 test_main()