blob: cc4c95bfc63c7f5e88d53dd1612f62ed9d229ee7 [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>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001070
1071 # Verify that character decoding errors throw exceptions instead
1072 # of crashing
1073 self.assertRaises(UnicodeDecodeError, parseString,
Guido van Rossum3e1f85e2007-07-27 18:03:11 +00001074 b'<fran\xe7ais>Comment \xe7a va ? Tr\xe8s bien ?</fran\xe7ais>')
Guido van Rossumd8faa362007-04-27 19:54:29 +00001075
1076 doc.unlink()
1077
1078 class UserDataHandler:
1079 called = 0
1080 def handle(self, operation, key, data, src, dst):
1081 dst.setUserData(key, data + 1, self)
1082 src.setUserData(key, None, None)
1083 self.called = 1
1084
1085 def testUserData(self):
1086 dom = Document()
1087 n = dom.createElement('e')
1088 self.confirm(n.getUserData("foo") is None)
1089 n.setUserData("foo", None, None)
1090 self.confirm(n.getUserData("foo") is None)
1091 n.setUserData("foo", 12, 12)
1092 n.setUserData("bar", 13, 13)
1093 self.confirm(n.getUserData("foo") == 12)
1094 self.confirm(n.getUserData("bar") == 13)
1095 n.setUserData("foo", None, None)
1096 self.confirm(n.getUserData("foo") is None)
1097 self.confirm(n.getUserData("bar") == 13)
1098
1099 handler = self.UserDataHandler()
1100 n.setUserData("bar", 12, handler)
1101 c = n.cloneNode(1)
1102 self.confirm(handler.called
1103 and n.getUserData("bar") is None
1104 and c.getUserData("bar") == 13)
1105 n.unlink()
1106 c.unlink()
1107 dom.unlink()
1108
1109 def checkRenameNodeSharedConstraints(self, doc, node):
1110 # Make sure illegal NS usage is detected:
1111 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, node,
1112 "http://xml.python.org/ns", "xmlns:foo")
1113 doc2 = parseString("<doc/>")
1114 self.assertRaises(xml.dom.WrongDocumentErr, doc2.renameNode, node,
1115 xml.dom.EMPTY_NAMESPACE, "foo")
1116
1117 def testRenameAttribute(self):
1118 doc = parseString("<doc a='v'/>")
1119 elem = doc.documentElement
1120 attrmap = elem.attributes
1121 attr = elem.attributes['a']
1122
1123 # Simple renaming
1124 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "b")
1125 self.confirm(attr.name == "b"
1126 and attr.nodeName == "b"
1127 and attr.localName is None
1128 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1129 and attr.prefix is None
1130 and attr.value == "v"
1131 and elem.getAttributeNode("a") is None
1132 and elem.getAttributeNode("b").isSameNode(attr)
1133 and attrmap["b"].isSameNode(attr)
1134 and attr.ownerDocument.isSameNode(doc)
1135 and attr.ownerElement.isSameNode(elem))
1136
1137 # Rename to have a namespace, no prefix
1138 attr = doc.renameNode(attr, "http://xml.python.org/ns", "c")
1139 self.confirm(attr.name == "c"
1140 and attr.nodeName == "c"
1141 and attr.localName == "c"
1142 and attr.namespaceURI == "http://xml.python.org/ns"
1143 and attr.prefix is None
1144 and attr.value == "v"
1145 and elem.getAttributeNode("a") is None
1146 and elem.getAttributeNode("b") is None
1147 and elem.getAttributeNode("c").isSameNode(attr)
1148 and elem.getAttributeNodeNS(
1149 "http://xml.python.org/ns", "c").isSameNode(attr)
1150 and attrmap["c"].isSameNode(attr)
1151 and attrmap[("http://xml.python.org/ns", "c")].isSameNode(attr))
1152
1153 # Rename to have a namespace, with prefix
1154 attr = doc.renameNode(attr, "http://xml.python.org/ns2", "p:d")
1155 self.confirm(attr.name == "p:d"
1156 and attr.nodeName == "p:d"
1157 and attr.localName == "d"
1158 and attr.namespaceURI == "http://xml.python.org/ns2"
1159 and attr.prefix == "p"
1160 and attr.value == "v"
1161 and elem.getAttributeNode("a") is None
1162 and elem.getAttributeNode("b") is None
1163 and elem.getAttributeNode("c") is None
1164 and elem.getAttributeNodeNS(
1165 "http://xml.python.org/ns", "c") is None
1166 and elem.getAttributeNode("p:d").isSameNode(attr)
1167 and elem.getAttributeNodeNS(
1168 "http://xml.python.org/ns2", "d").isSameNode(attr)
1169 and attrmap["p:d"].isSameNode(attr)
1170 and attrmap[("http://xml.python.org/ns2", "d")].isSameNode(attr))
1171
1172 # Rename back to a simple non-NS node
1173 attr = doc.renameNode(attr, xml.dom.EMPTY_NAMESPACE, "e")
1174 self.confirm(attr.name == "e"
1175 and attr.nodeName == "e"
1176 and attr.localName is None
1177 and attr.namespaceURI == xml.dom.EMPTY_NAMESPACE
1178 and attr.prefix is None
1179 and attr.value == "v"
1180 and elem.getAttributeNode("a") is None
1181 and elem.getAttributeNode("b") is None
1182 and elem.getAttributeNode("c") is None
1183 and elem.getAttributeNode("p:d") is None
1184 and elem.getAttributeNodeNS(
1185 "http://xml.python.org/ns", "c") is None
1186 and elem.getAttributeNode("e").isSameNode(attr)
1187 and attrmap["e"].isSameNode(attr))
1188
1189 self.assertRaises(xml.dom.NamespaceErr, doc.renameNode, attr,
1190 "http://xml.python.org/ns", "xmlns")
1191 self.checkRenameNodeSharedConstraints(doc, attr)
1192 doc.unlink()
1193
1194 def testRenameElement(self):
1195 doc = parseString("<doc/>")
1196 elem = doc.documentElement
1197
1198 # Simple renaming
1199 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "a")
1200 self.confirm(elem.tagName == "a"
1201 and elem.nodeName == "a"
1202 and elem.localName is None
1203 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1204 and elem.prefix is None
1205 and elem.ownerDocument.isSameNode(doc))
1206
1207 # Rename to have a namespace, no prefix
1208 elem = doc.renameNode(elem, "http://xml.python.org/ns", "b")
1209 self.confirm(elem.tagName == "b"
1210 and elem.nodeName == "b"
1211 and elem.localName == "b"
1212 and elem.namespaceURI == "http://xml.python.org/ns"
1213 and elem.prefix is None
1214 and elem.ownerDocument.isSameNode(doc))
1215
1216 # Rename to have a namespace, with prefix
1217 elem = doc.renameNode(elem, "http://xml.python.org/ns2", "p:c")
1218 self.confirm(elem.tagName == "p:c"
1219 and elem.nodeName == "p:c"
1220 and elem.localName == "c"
1221 and elem.namespaceURI == "http://xml.python.org/ns2"
1222 and elem.prefix == "p"
1223 and elem.ownerDocument.isSameNode(doc))
1224
1225 # Rename back to a simple non-NS node
1226 elem = doc.renameNode(elem, xml.dom.EMPTY_NAMESPACE, "d")
1227 self.confirm(elem.tagName == "d"
1228 and elem.nodeName == "d"
1229 and elem.localName is None
1230 and elem.namespaceURI == xml.dom.EMPTY_NAMESPACE
1231 and elem.prefix is None
1232 and elem.ownerDocument.isSameNode(doc))
1233
1234 self.checkRenameNodeSharedConstraints(doc, elem)
1235 doc.unlink()
1236
1237 def testRenameOther(self):
1238 # We have to create a comment node explicitly since not all DOM
1239 # builders used with minidom add comments to the DOM.
1240 doc = xml.dom.minidom.getDOMImplementation().createDocument(
1241 xml.dom.EMPTY_NAMESPACE, "e", None)
1242 node = doc.createComment("comment")
1243 self.assertRaises(xml.dom.NotSupportedErr, doc.renameNode, node,
1244 xml.dom.EMPTY_NAMESPACE, "foo")
1245 doc.unlink()
1246
1247 def testWholeText(self):
1248 doc = parseString("<doc>a</doc>")
1249 elem = doc.documentElement
1250 text = elem.childNodes[0]
Ezio Melottib3aedd42010-11-20 19:04:17 +00001251 self.assertEqual(text.nodeType, Node.TEXT_NODE)
Guido van Rossumd8faa362007-04-27 19:54:29 +00001252
1253 self.checkWholeText(text, "a")
1254 elem.appendChild(doc.createTextNode("b"))
1255 self.checkWholeText(text, "ab")
1256 elem.insertBefore(doc.createCDATASection("c"), text)
1257 self.checkWholeText(text, "cab")
1258
1259 # make sure we don't cross other nodes
1260 splitter = doc.createComment("comment")
1261 elem.appendChild(splitter)
1262 text2 = doc.createTextNode("d")
1263 elem.appendChild(text2)
1264 self.checkWholeText(text, "cab")
1265 self.checkWholeText(text2, "d")
1266
1267 x = doc.createElement("x")
1268 elem.replaceChild(x, splitter)
1269 splitter = x
1270 self.checkWholeText(text, "cab")
1271 self.checkWholeText(text2, "d")
1272
1273 x = doc.createProcessingInstruction("y", "z")
1274 elem.replaceChild(x, splitter)
1275 splitter = x
1276 self.checkWholeText(text, "cab")
1277 self.checkWholeText(text2, "d")
1278
1279 elem.removeChild(splitter)
1280 self.checkWholeText(text, "cabd")
1281 self.checkWholeText(text2, "cabd")
1282
1283 def testPatch1094164(self):
1284 doc = parseString("<doc><e/></doc>")
1285 elem = doc.documentElement
1286 e = elem.firstChild
1287 self.confirm(e.parentNode is elem, "Before replaceChild()")
1288 # Check that replacing a child with itself leaves the tree unchanged
1289 elem.replaceChild(e, e)
1290 self.confirm(e.parentNode is elem, "After replaceChild()")
1291
1292 def testReplaceWholeText(self):
1293 def setup():
1294 doc = parseString("<doc>a<e/>d</doc>")
1295 elem = doc.documentElement
1296 text1 = elem.firstChild
1297 text2 = elem.lastChild
1298 splitter = text1.nextSibling
1299 elem.insertBefore(doc.createTextNode("b"), splitter)
1300 elem.insertBefore(doc.createCDATASection("c"), text1)
1301 return doc, elem, text1, splitter, text2
1302
1303 doc, elem, text1, splitter, text2 = setup()
1304 text = text1.replaceWholeText("new content")
1305 self.checkWholeText(text, "new content")
1306 self.checkWholeText(text2, "d")
1307 self.confirm(len(elem.childNodes) == 3)
1308
1309 doc, elem, text1, splitter, text2 = setup()
1310 text = text2.replaceWholeText("new content")
1311 self.checkWholeText(text, "new content")
1312 self.checkWholeText(text1, "cab")
1313 self.confirm(len(elem.childNodes) == 5)
1314
1315 doc, elem, text1, splitter, text2 = setup()
1316 text = text1.replaceWholeText("")
1317 self.checkWholeText(text2, "d")
1318 self.confirm(text is None
1319 and len(elem.childNodes) == 2)
1320
1321 def testSchemaType(self):
1322 doc = parseString(
1323 "<!DOCTYPE doc [\n"
1324 " <!ENTITY e1 SYSTEM 'http://xml.python.org/e1'>\n"
1325 " <!ENTITY e2 SYSTEM 'http://xml.python.org/e2'>\n"
1326 " <!ATTLIST doc id ID #IMPLIED \n"
1327 " ref IDREF #IMPLIED \n"
1328 " refs IDREFS #IMPLIED \n"
1329 " enum (a|b) #IMPLIED \n"
1330 " ent ENTITY #IMPLIED \n"
1331 " ents ENTITIES #IMPLIED \n"
1332 " nm NMTOKEN #IMPLIED \n"
1333 " nms NMTOKENS #IMPLIED \n"
1334 " text CDATA #IMPLIED \n"
1335 " >\n"
1336 "]><doc id='name' notid='name' text='splat!' enum='b'"
1337 " ref='name' refs='name name' ent='e1' ents='e1 e2'"
1338 " nm='123' nms='123 abc' />")
1339 elem = doc.documentElement
1340 # We don't want to rely on any specific loader at this point, so
1341 # just make sure we can get to all the names, and that the
1342 # DTD-based namespace is right. The names can vary by loader
1343 # since each supports a different level of DTD information.
1344 t = elem.schemaType
1345 self.confirm(t.name is None
1346 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1347 names = "id notid text enum ref refs ent ents nm nms".split()
1348 for name in names:
1349 a = elem.getAttributeNode(name)
1350 t = a.schemaType
1351 self.confirm(hasattr(t, "name")
1352 and t.namespace == xml.dom.EMPTY_NAMESPACE)
1353
1354 def testSetIdAttribute(self):
1355 doc = parseString("<doc a1='v' a2='w'/>")
1356 e = doc.documentElement
1357 a1 = e.getAttributeNode("a1")
1358 a2 = e.getAttributeNode("a2")
1359 self.confirm(doc.getElementById("v") is None
1360 and not a1.isId
1361 and not a2.isId)
1362 e.setIdAttribute("a1")
1363 self.confirm(e.isSameNode(doc.getElementById("v"))
1364 and a1.isId
1365 and not a2.isId)
1366 e.setIdAttribute("a2")
1367 self.confirm(e.isSameNode(doc.getElementById("v"))
1368 and e.isSameNode(doc.getElementById("w"))
1369 and a1.isId
1370 and a2.isId)
1371 # replace the a1 node; the new node should *not* be an ID
1372 a3 = doc.createAttribute("a1")
1373 a3.value = "v"
1374 e.setAttributeNode(a3)
1375 self.confirm(doc.getElementById("v") is None
1376 and e.isSameNode(doc.getElementById("w"))
1377 and not a1.isId
1378 and a2.isId
1379 and not a3.isId)
1380 # renaming an attribute should not affect its ID-ness:
1381 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1382 self.confirm(e.isSameNode(doc.getElementById("w"))
1383 and a2.isId)
1384
1385 def testSetIdAttributeNS(self):
1386 NS1 = "http://xml.python.org/ns1"
1387 NS2 = "http://xml.python.org/ns2"
1388 doc = parseString("<doc"
1389 " xmlns:ns1='" + NS1 + "'"
1390 " xmlns:ns2='" + NS2 + "'"
1391 " ns1:a1='v' ns2:a2='w'/>")
1392 e = doc.documentElement
1393 a1 = e.getAttributeNodeNS(NS1, "a1")
1394 a2 = e.getAttributeNodeNS(NS2, "a2")
1395 self.confirm(doc.getElementById("v") is None
1396 and not a1.isId
1397 and not a2.isId)
1398 e.setIdAttributeNS(NS1, "a1")
1399 self.confirm(e.isSameNode(doc.getElementById("v"))
1400 and a1.isId
1401 and not a2.isId)
1402 e.setIdAttributeNS(NS2, "a2")
1403 self.confirm(e.isSameNode(doc.getElementById("v"))
1404 and e.isSameNode(doc.getElementById("w"))
1405 and a1.isId
1406 and a2.isId)
1407 # replace the a1 node; the new node should *not* be an ID
1408 a3 = doc.createAttributeNS(NS1, "a1")
1409 a3.value = "v"
1410 e.setAttributeNode(a3)
1411 self.confirm(e.isSameNode(doc.getElementById("w")))
1412 self.confirm(not a1.isId)
1413 self.confirm(a2.isId)
1414 self.confirm(not a3.isId)
1415 self.confirm(doc.getElementById("v") is None)
1416 # renaming an attribute should not affect its ID-ness:
1417 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1418 self.confirm(e.isSameNode(doc.getElementById("w"))
1419 and a2.isId)
1420
1421 def testSetIdAttributeNode(self):
1422 NS1 = "http://xml.python.org/ns1"
1423 NS2 = "http://xml.python.org/ns2"
1424 doc = parseString("<doc"
1425 " xmlns:ns1='" + NS1 + "'"
1426 " xmlns:ns2='" + NS2 + "'"
1427 " ns1:a1='v' ns2:a2='w'/>")
1428 e = doc.documentElement
1429 a1 = e.getAttributeNodeNS(NS1, "a1")
1430 a2 = e.getAttributeNodeNS(NS2, "a2")
1431 self.confirm(doc.getElementById("v") is None
1432 and not a1.isId
1433 and not a2.isId)
1434 e.setIdAttributeNode(a1)
1435 self.confirm(e.isSameNode(doc.getElementById("v"))
1436 and a1.isId
1437 and not a2.isId)
1438 e.setIdAttributeNode(a2)
1439 self.confirm(e.isSameNode(doc.getElementById("v"))
1440 and e.isSameNode(doc.getElementById("w"))
1441 and a1.isId
1442 and a2.isId)
1443 # replace the a1 node; the new node should *not* be an ID
1444 a3 = doc.createAttributeNS(NS1, "a1")
1445 a3.value = "v"
1446 e.setAttributeNode(a3)
1447 self.confirm(e.isSameNode(doc.getElementById("w")))
1448 self.confirm(not a1.isId)
1449 self.confirm(a2.isId)
1450 self.confirm(not a3.isId)
1451 self.confirm(doc.getElementById("v") is None)
1452 # renaming an attribute should not affect its ID-ness:
1453 doc.renameNode(a2, xml.dom.EMPTY_NAMESPACE, "an")
1454 self.confirm(e.isSameNode(doc.getElementById("w"))
1455 and a2.isId)
1456
1457 def testPickledDocument(self):
1458 doc = parseString("<?xml version='1.0' encoding='us-ascii'?>\n"
1459 "<!DOCTYPE doc PUBLIC 'http://xml.python.org/public'"
1460 " 'http://xml.python.org/system' [\n"
1461 " <!ELEMENT e EMPTY>\n"
1462 " <!ENTITY ent SYSTEM 'http://xml.python.org/entity'>\n"
1463 "]><doc attr='value'> text\n"
1464 "<?pi sample?> <!-- comment --> <e/> </doc>")
1465 s = pickle.dumps(doc)
1466 doc2 = pickle.loads(s)
1467 stack = [(doc, doc2)]
1468 while stack:
1469 n1, n2 = stack.pop()
1470 self.confirm(n1.nodeType == n2.nodeType
1471 and len(n1.childNodes) == len(n2.childNodes)
1472 and n1.nodeName == n2.nodeName
1473 and not n1.isSameNode(n2)
1474 and not n2.isSameNode(n1))
1475 if n1.nodeType == Node.DOCUMENT_TYPE_NODE:
1476 len(n1.entities)
1477 len(n2.entities)
1478 len(n1.notations)
1479 len(n2.notations)
1480 self.confirm(len(n1.entities) == len(n2.entities)
1481 and len(n1.notations) == len(n2.notations))
1482 for i in range(len(n1.notations)):
Georg Brandl89fad142010-03-14 10:23:39 +00001483 # XXX this loop body doesn't seem to be executed?
Guido van Rossumd8faa362007-04-27 19:54:29 +00001484 no1 = n1.notations.item(i)
1485 no2 = n1.notations.item(i)
1486 self.confirm(no1.name == no2.name
1487 and no1.publicId == no2.publicId
1488 and no1.systemId == no2.systemId)
Georg Brandl89fad142010-03-14 10:23:39 +00001489 stack.append((no1, no2))
Guido van Rossumd8faa362007-04-27 19:54:29 +00001490 for i in range(len(n1.entities)):
1491 e1 = n1.entities.item(i)
1492 e2 = n2.entities.item(i)
1493 self.confirm(e1.notationName == e2.notationName
1494 and e1.publicId == e2.publicId
1495 and e1.systemId == e2.systemId)
1496 stack.append((e1, e2))
1497 if n1.nodeType != Node.DOCUMENT_NODE:
1498 self.confirm(n1.ownerDocument.isSameNode(doc)
1499 and n2.ownerDocument.isSameNode(doc2))
1500 for i in range(len(n1.childNodes)):
1501 stack.append((n1.childNodes[i], n2.childNodes[i]))
1502
Benjamin Peterson2b7411d2008-05-26 17:36:47 +00001503 def testSerializeCommentNodeWithDoubleHyphen(self):
1504 doc = create_doc_without_doctype()
1505 doc.appendChild(doc.createComment("foo--bar"))
1506 self.assertRaises(ValueError, doc.toxml)
1507
Benjamin Peterson863a0c32011-03-02 23:40:36 +00001508
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001509 def testEmptyXMLNSValue(self):
1510 doc = parseString("<element xmlns=''>\n"
1511 "<foo/>\n</element>")
1512 doc2 = parseString(doc.toxml())
1513 self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE)
1514
Raymond Hettinger06eef9c2011-06-25 15:54:52 +02001515 def testDocRemoveChild(self):
1516 doc = parse(tstfile)
1517 title_tag = doc.documentElement.getElementsByTagName("TITLE")[0]
1518 self.assertRaises( xml.dom.NotFoundErr, doc.removeChild, title_tag)
1519 num_children_before = len(doc.childNodes)
1520 doc.removeChild(doc.childNodes[0])
1521 num_children_after = len(doc.childNodes)
1522 self.assertTrue(num_children_after == num_children_before - 1)
Georg Brandlb9cd72a2010-10-15 17:58:45 +00001523
Guido van Rossumd8faa362007-04-27 19:54:29 +00001524def test_main():
1525 run_unittest(MinidomTest)
1526
1527if __name__ == "__main__":
1528 test_main()