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