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