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